Lorsque vous choisissez des tailles de police importantes dans le panneau de configuration Windows (125 ou 150%, par exemple), une application VCL pose des problèmes chaque fois que vous définissez une valeur pixel par pixel.
Prendre la TStatusBar.Panel
. J'ai réglé sa largeur de sorte qu'elle contienne exactement une étiquette. Désormais, avec les grosses polices, l'étiquette "déborde". Même problème avec d'autres composants.
Certains nouveaux ordinateurs portables de Dell sont déjà livrés avec un paramètre par défaut de 125%. Par conséquent, ce problème était plutôt rare dans le passé, mais il est vraiment important.
Que peut-on faire pour surmonter ce problème?
Remarque: veuillez consulter les autres réponses car elles contiennent des techniques très utiles. Ma réponse ne contient que des mises en garde et des mises en garde contre l'hypothèse que la prise de conscience DPI est facile.
J'évite généralement la mise à l'échelle DPI avec TForm.Scaled = True
. La sensibilisation à la DPI n’est importante pour moi que si elle le devient pour les clients qui me téléphonent et qui sont prêts à payer pour cela. La raison technique derrière ce point de vue est que, qu’il soit conscient de l’information ou non, vous ouvrez une fenêtre sur un monde de blessures. De nombreux contrôles VCL standard et tiers ne fonctionnent pas correctement avec une résolution élevée. L'exception notable, à savoir que les composants VCL qui englobent les contrôles communs Windows, fonctionne remarquablement bien à une résolution élevée. Un grand nombre de contrôles personnalisés Delphi VCL intégrés et tiers ne fonctionnent pas bien, ou pas du tout, à un DPI élevé. Si vous envisagez d'activer TForm.Scaled, assurez-vous de tester à 96, 125 et 150 DPI pour chaque formulaire de votre projet, ainsi que pour chaque contrôle tiers et intégré que vous utilisez.
Delphi lui-même est écrit en Delphi. Le drapeau de sensibilisation élevée à DPI est activé pour la plupart des formulaires, bien que, même récemment, dans Delphi XE2, les auteurs de IDE eux-mêmes aient décidé de NE PAS activer ce drapeau. Notez que dans Delphi XE4 et versions ultérieures, l'indicateur de reconnaissance HIGH DPI est activé et le IDE a bonne apparence.
Je suggère que vous n'utilisiez pas TForm.Scaled = true (ce qui est un défaut dans Delphi. Par conséquent, sauf si vous l'avez modifié, la plupart de vos formulaires ont Scaled = true) avec les indicateurs High DPI Aware (comme indiqué dans les réponses de David) avec Applications VCL construites à l'aide du concepteur de formulaires intégré de Delphi.
Dans le passé, j'ai essayé de créer un échantillon minimal du type de casse que vous pouvez vous attendre à voir quand TForm.Scaled est vrai, et lorsque la mise à l'échelle d'un formulaire Delphi présente un problème. Ces problèmes ne sont pas toujours et uniquement déclenchés par une valeur DPI autre que 96. Je n'ai pas pu déterminer une liste complète d'autres éléments, notamment les modifications de la taille de la police Windows XP. Mais comme la plupart de ces problèmes ne figurent que dans mes propres applications, dans des situations assez complexes, j'ai décidé de vous montrer des preuves que vous pouvez vérifier vous-même.
Delphi XE ressemble à ceci lorsque vous définissez la résolution DPI sur "Fonts @ 200%" dans Windows 7 et que Delphi XE2 est également défectueux sous Windows 7 et 8, mais ces problèmes semblent être résolus à partir de Delphi XE4:
Ce sont principalement des contrôles VCL standard qui se comportent mal à une DPI élevée. Notez que la plupart des éléments n'ont pas du tout été redimensionnés. Par conséquent, les développeurs de Delphi IDE ont décidé d'ignorer la prise en compte de la DPI, ainsi que de désactiver la virtualisation DPI. Un tel choix intéressant.
Désactivez la virtualisation DPI uniquement si vous souhaitez cette nouvelle source supplémentaire de problèmes et de choix difficiles. Je vous suggère de le laisser seul. Notez que les contrôles communs Windows semblent généralement fonctionner correctement. Notez que le contrôle Delphi Data-Explorer est un wrapper C # WinForms autour d'un contrôle commun de l'arborescence Windows. C’est un pur problème Microsoft. Pour le corriger, Embarcadero devra peut-être réécrire un contrôle d’arborescence .Net pur pour leur Explorateur de données ou écrire un code DPI-check-and-modify-properties pour modifier la hauteur des éléments dans le contrôle. Même Microsoft WinForms ne peut pas gérer les DPI élevés de manière propre, automatique et sans code kludge personnalisé.
Mise à jour: Fait facto intéressant: Bien que delphi IDE ne semble pas être "virtualisé", il n'utilise pas le contenu du manifeste affiché par David pour réaliser une "virtualisation non DPI". Peut-être utilise-t-il une fonction API au moment de l'exécution.
Mise à jour 2: En réponse à la manière dont je prendrais en charge un DPI à 100%/125%, je proposerais un plan en deux phases. La phase 1 consiste à inventorier mon code pour les contrôles personnalisés à corriger pour une DPI élevée, puis à définir un plan pour les corriger ou les éliminer progressivement. La phase 2 consisterait à prendre certaines zones de mon code conçues comme des formulaires sans gestion de la mise en forme et à les remplacer par des formulaires utilisant une sorte de gestion de la mise en forme afin que les modifications de DPI ou de hauteur de police puissent fonctionner sans découpage. Je suppose que ce travail de mise en page "inter-contrôle" serait beaucoup plus complexe dans la plupart des applications que le travail "intra-contrôle".
Mise à jour: En 2016, la dernière version de Delphi 10.1 Berlin fonctionne bien sur mon poste de travail à 150 dpi.
Vos paramètres dans le fichier .dfm seront mis à l'échelle correctement, tant que Scaled
est True
.
Si vous définissez des dimensions dans le code, vous devez les redimensionner par Screen.PixelsPerInch
, Divisé par Form.PixelsPerInch
. Utilisez MulDiv
pour le faire.
function TMyForm.ScaleDimension(const X: Integer): Integer;
begin
Result := MulDiv(X, Screen.PixelsPerInch, PixelsPerInch);
end;
C'est ce que fait la structure de persistance de formulaire lorsque Scaled
est True
.
En fait, vous pouvez argumenter de manière pertinente pour remplacer cette fonction par une version codant en dur une valeur de 96 pour le dénominateur. Cela vous permet d'utiliser des valeurs de dimension absolues sans vous soucier du changement de signification si vous modifiez la mise à l'échelle des polices sur votre machine de développement et réenregistrez le fichier .dfm. La raison qui importe est que la propriété PixelsPerInch
stockée dans le fichier .dfm est la valeur de la machine sur laquelle le fichier .dfm a été enregistré pour la dernière fois.
const
SmallFontsPixelsPerInch = 96;
function ScaleFromSmallFontsDimension(const X: Integer): Integer;
begin
Result := MulDiv(X, Screen.PixelsPerInch, SmallFontsPixelsPerInch);
end;
Donc, pour continuer sur le thème, il faut également se méfier du fait que si votre projet est développé sur plusieurs machines avec des valeurs DPI différentes, vous constaterez que la mise à l'échelle utilisée par Delphi lors de l'enregistrement de fichiers .dfm entraîne des contrôles errants au fil d'une série de modifications. . Sur mon lieu de travail, pour éviter cela, nous appliquons une règle stricte selon laquelle les formulaires ne sont édités qu’à 96 ppp (mise à l’échelle à 100%).
En fait, ma version de ScaleFromSmallFontsDimension
tient également compte de la possibilité que la police du formulaire diffère au moment de l'exécution de celle définie au moment de la conception. Sur XP les formulaires de mon application utilisent 8pt Tahoma. Sous Vista et à partir de 9pt. Segoe UI est utilisé. Ceci fournit un autre degré de liberté. La mise à l'échelle doit en tenir compte, car les valeurs de dimension absolue utilisées dans le code source est supposé être relatif à la ligne de base de 8 pt Tahoma à 96 dpi.
Si vous utilisez des images ou des glyphes dans votre interface utilisateur, ceux-ci doivent également être redimensionnés. Un exemple courant serait les glyphes utilisés dans les barres d’outils et les menus. Vous voudrez fournir ces glyphes sous forme de ressources d'icônes liées à votre exécutable. Chaque icône doit contenir une gamme de tailles. Au moment de l'exécution, vous devez choisir la taille la plus appropriée et la charger dans une liste d'images. Quelques détails sur ce sujet peuvent être trouvés ici: Comment charger des icônes à partir d’une ressource sans aliasing?
Une autre astuce utile consiste à définir des dimensions en unités relatives, relatives à TextWidth
ou TextHeight
. Donc, si vous voulez que quelque chose soit autour de 10 lignes verticales, vous pouvez utiliser 10*Canvas.TextHeight('Ag')
. C'est une métrique très approximative car elle ne permet pas l'interligne, etc. Cependant, tout ce que vous avez à faire est souvent capable de vous assurer que l'interface graphique évolue correctement avec PixelsPerInch
.
Vous devez également marquer votre application comme étant prise en charge élevée . La meilleure façon de procéder consiste à utiliser le manifeste d'application. Comme les outils de construction de Delphi ne vous permettent pas de personnaliser le manifeste que vous utilisez, cela vous oblige à lier votre propre ressource de manifeste.
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<Assembly xmlns="urn:schemas-Microsoft-com:asm.v1" manifestVersion="1.0">
<asmv3:application xmlns:asmv3="urn:schemas-Microsoft-com:asm.v3">
<asmv3:windowsSettings
xmlns="http://schemas.Microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</Assembly>
Le script de ressource ressemble à ceci:
1 24 "Manifest.txt"
où Manifest.txt
contient le manifeste réel. Vous devez également inclure la section comctl32 v6 et définir requestedExecutionLevel
sur asInvoker
. Vous liez ensuite cette ressource compilée à votre application et assurez-vous que Delphi n'essaie pas de faire la même chose avec son manifeste. Dans Delphi moderne, vous y parvenez en définissant l'option de projet Thèmes d'exécution à Aucune.
Le manifeste est le moyen right de déclarer votre application très sensible à la DPI. Si vous souhaitez simplement l'essayer rapidement sans modifier votre manifeste, appelez SetProcessDPIAware
. Faites-le dès la première action de votre application. De préférence dans l'une des premières sections d'initialisation de l'unité ou en tant que première chose dans votre fichier .dpr.
Si vous ne déclarez pas que votre application prend en charge une résolution DPI élevée, Vista et versions ultérieures la rendent dans un mode hérité pour toute police dont l'échelle est supérieure à 125%. Cela semble assez terrible. Essayez d'éviter de tomber dans ce piège.
Windows 8.1 par mise à jour DPI du moniteur
Depuis Windows 8.1, le système d'exploitation prend désormais en charge les paramètres DPI par moniteur ( http://msdn.Microsoft.com/en-ca/magazine/dn574798.aspx ). Ceci est un gros problème pour les appareils modernes qui peuvent avoir différents écrans associés avec des capacités très différentes. Vous avez peut-être un écran d'ordinateur portable à très haute résolution et un projecteur externe à faible résolution. Soutenir un tel scénario prend encore plus de travail que décrit ci-dessus.
Il est également important de noter que le respect de la DPI de l'utilisateur n'est qu'un sous-ensemble de votre travail réel:
respecter la taille de la police de l'utilisateur
Pendant des décennies, Windows a résolu ce problème en effectuant une mise en page à l'aide de Dialog Units, plutôt que de pixels. Une "unité de dialogue" est définie pour que le caractère moyen de la police soit
Delphi est livré avec une notion (buggy) de Scaled
, où un formulaire tente de s’ajuster automatiquement en fonction de la
Cela ne résout pas le problème lorsque l'utilisateur utilise une police différente de celle avec laquelle vous avez conçu le formulaire, par exemple:
6.21px x 13.00px
, à 96 dpi).utilisateur fonctionnant avec Tahoma 8pt (où le caractère moyen est 5.94px x 13.00px
, à 96 dpi)
Comme ce fut le cas pour toute personne développant une application pour Windows 2000 ou Windows XP.
ou
5.94px x 13.00px
, à 96 dpi).6.67px x 15px
, à 96 dpi)En tant que bon développeur, vous allez respecter les préférences de police de votre utilisateur. Cela signifie que vous devez également adapter tous les contrôles de votre formulaire à la nouvelle taille de police:
Scaled
ne gérera pas cela pour vous.
Il y a pire quand:
10.52px x 25px
Maintenant, il faut tout mettre à l'échelle
Scaled
ne gérera pas cela pour vous.
Si vous êtes malin, vous pouvez voir à quel point le respect de DPI est irrévocable:
Vous ne devriez pas regarder le paramètre DPI de l'utilisateur, mais plutôt sa taille de police . Deux utilisateurs en cours d'exécution
utilisent la même police . DPI est juste un chose qui affecte la taille de la police; les préférences de l'utilisateur sont l'autre.
Clovis a remarqué que je référence une fonction StandardizeFormFont
qui corrige la police dans un formulaire et la redimensionne à la nouvelle taille de police. Ce n'est pas une fonction standard, mais tout un ensemble de fonctions qui accomplissent la tâche simple que Borland n'a jamais effectuée.
function StandardizeFormFont(AForm: TForm): Real;
var
preferredFontName: string;
preferredFontHeight: Integer;
begin
GetUserFontPreference({out}preferredFontName, {out}preferredFontHeight);
//e.g. "Segoe UI",
Result := Toolkit.StandardizeFormFont(AForm, PreferredFontName, PreferredFontHeight);
end;
Windows a 6 polices différentes; il n'y a pas de "paramètre de police" unique dans Windows.
Mais nous savons par expérience que nos formulaires doivent suivre le réglage Icon Title Font
procedure GetUserFontPreference(out FaceName: string; out PixelHeight: Integer);
var
font: TFont;
begin
font := Toolkit.GetIconTitleFont;
try
FaceName := font.Name; //e.g. "Segoe UI"
//Dogfood testing: use a larger font than we're used to; to force us to actually test it
if IsDebuggerPresent then
font.Size := font.Size+1;
PixelHeight := font.Height; //e.g. -16
finally
font.Free;
end;
end;
Une fois que nous connaissons la taille de la police, nous redimensionnons le formulaire to, nous obtenons la hauteur de police actuelle du formulaire (en pixels) et le redimensionnons en fonction de ce facteur.
Par exemple, si je mets le formulaire à -16
, et que le formulaire est actuellement à -11
, nous devons redimensionner le forme entière par:
-16 / -11 = 1.45454%
La normalisation se déroule en deux phases. Commencez par redimensionner la forme en fonction du nouveau: tailles de police anciennes. Ensuite, changez les contrôles (de manière récursive) pour utiliser la nouvelle police.
function StandardizeFormFont(AForm: TForm; FontName: string; FontHeight: Integer): Real;
var
oldHeight: Integer;
begin
Assert(Assigned(AForm));
if (AForm.Scaled) then
begin
OutputDebugString(PChar('WARNING: StandardizeFormFont: Form "'+GetControlName(AForm)+'" is set to Scaled. Proper form scaling requires VCL scaling to be disabled, unless you implement scaling by overriding the protected ChangeScale() method of the form.'));
end;
if (AForm.AutoScroll) then
begin
if AForm.WindowState = wsNormal then
begin
OutputDebugString(PChar('WARNING: StandardizeFormFont: Form "'+GetControlName(AForm)+'" is set to AutoScroll. Form designed size will be suseptable to changes in Windows form caption height (e.g. 2000 vs XP).'));
if IsDebuggerPresent then
Windows.DebugBreak; //Some forms would like it (to fix maximizing problem)
end;
end;
if (not AForm.ShowHint) then
begin
AForm.ShowHint := True;
OutputDebugString(PChar('INFORMATION: StandardizeFormFont: Turning on form "'+GetControlName(AForm)+'" hints. (ShowHint := True)'));
if IsDebuggerPresent then
Windows.DebugBreak; //Some forms would like it (to fix maximizing problem)
end;
oldHeight := AForm.Font.Height;
//Scale the form to the new font size
// if (FontHeight <> oldHeight) then For compatibility, it's safer to trigger a call to ChangeScale, since a lot of people will be assuming it always is called
begin
ScaleForm(AForm, FontHeight, oldHeight);
end;
//Now change all controls to actually use the new font
Toolkit.StandardizeFont_ControlCore(AForm, g_ForceClearType, FontName, FontHeight,
AForm.Font.Name, AForm.Font.Size);
//Return the scaling ratio, so any hard-coded values can be multiplied
Result := FontHeight / oldHeight;
end;
Voici le travail de mise à l'échelle réelle d'un formulaire. Cela fonctionne autour des bugs de la propre méthode Form.ScaleBy
De Borland. Il doit d'abord désactiver toutes les ancres du formulaire, puis effectuer la mise à l'échelle, puis réactiver les ancres:
TAnchorsArray = array of TAnchors;
procedure ScaleForm(const AForm: TForm; const M, D: Integer);
var
aAnchorStorage: TAnchorsArray;
RectBefore, RectAfter: TRect;
x, y: Integer;
monitorInfo: TMonitorInfo;
workArea: TRect;
begin
if (M = 0) and (D = 0) then
Exit;
RectBefore := AForm.BoundsRect;
SetLength(aAnchorStorage, 0);
aAnchorStorage := DisableAnchors(AForm);
try
AForm.ScaleBy(M, D);
finally
EnableAnchors(AForm, aAnchorStorage);
end;
RectAfter := AForm.BoundsRect;
case AForm.Position of
poScreenCenter, poDesktopCenter, poMainFormCenter, poOwnerFormCenter,
poDesigned: //i think i really want everything else to also follow the nudging rules...why did i exclude poDesigned
begin
//This was only nudging by one quarter the difference, rather than one half the difference
// x := RectAfter.Left - ((RectAfter.Right-RectBefore.Right) div 2);
// y := RectAfter.Top - ((RectAfter.Bottom-RectBefore.Bottom) div 2);
x := RectAfter.Left - ((RectAfter.Right-RectAfter.Left) - (RectBefore.Right-RectBefore.Left)) div 2;
y := RectAfter.Top - ((RectAfter.Bottom-RectAfter.Top)-(RectBefore.Bottom-RectBefore.Top)) div 2;
end;
else
//poDesigned, poDefault, poDefaultPosOnly, poDefaultSizeOnly:
x := RectAfter.Left;
y := RectAfter.Top;
end;
if AForm.Monitor <> nil then
begin
monitorInfo.cbSize := SizeOf(monitorInfo);
if GetMonitorInfo(AForm.Monitor.Handle, @monitorInfo) then
workArea := monitorInfo.rcWork
else
begin
OutputDebugString(PChar(SysErrorMessage(GetLastError)));
workArea := Rect(AForm.Monitor.Left, AForm.Monitor.Top, AForm.Monitor.Left+AForm.Monitor.Width, AForm.Monitor.Top+AForm.Monitor.Height);
end;
// If the form is off the right or bottom of the screen then we need to pull it back
if RectAfter.Right > workArea.Right then
x := workArea.Right - (RectAfter.Right-RectAfter.Left); //rightEdge - widthOfForm
if RectAfter.Bottom > workArea.Bottom then
y := workArea.Bottom - (RectAfter.Bottom-RectAfter.Top); //bottomEdge - heightOfForm
x := Max(x, workArea.Left); //don't go beyond left Edge
y := Max(y, workArea.Top); //don't go above top Edge
end
else
begin
x := Max(x, 0); //don't go beyond left Edge
y := Max(y, 0); //don't go above top Edge
end;
AForm.SetBounds(x, y,
RectAfter.Right-RectAfter.Left, //Width
RectAfter.Bottom-RectAfter.Top); //Height
end;
et puis nous devons effectivement récursivement tiliser la nouvelle police:
procedure StandardizeFont_ControlCore(AControl: TControl; ForceClearType: Boolean;
FontName: string; FontSize: Integer;
ForceFontIfName: string; ForceFontIfSize: Integer);
const
CLEARTYPE_QUALITY = 5;
var
i: Integer;
RunComponent: TComponent;
AControlFont: TFont;
begin
if not Assigned(AControl) then
Exit;
if (AControl is TStatusBar) then
begin
TStatusBar(AControl).UseSystemFont := False; //force...
TStatusBar(AControl).UseSystemFont := True; //...it
end
else
begin
AControlFont := Toolkit.GetControlFont(AControl);
if not Assigned(AControlFont) then
Exit;
StandardizeFont_ControlFontCore(AControlFont, ForceClearType,
FontName, FontSize,
ForceFontIfName, ForceFontIfSize);
end;
{ If a panel has a toolbar on it, the toolbar won't Paint properly. So this idea won't work.
if (not Toolkit.IsRemoteSession) and (AControl is TWinControl) and (not (AControl is TToolBar)) then
TWinControl(AControl).DoubleBuffered := True;
}
//Iterate children
for i := 0 to AControl.ComponentCount-1 do
begin
RunComponent := AControl.Components[i];
if RunComponent is TControl then
StandardizeFont_ControlCore(
TControl(RunComponent), ForceClearType,
FontName, FontSize,
ForceFontIfName, ForceFontIfSize);
end;
end;
Les ancres étant désactivées de manière récursive:
function DisableAnchors(ParentControl: TWinControl): TAnchorsArray;
var
StartingIndex: Integer;
begin
StartingIndex := 0;
DisableAnchors_Core(ParentControl, Result, StartingIndex);
end;
procedure DisableAnchors_Core(ParentControl: TWinControl; var aAnchorStorage: TAnchorsArray; var StartingIndex: Integer);
var
iCounter: integer;
ChildControl: TControl;
begin
if (StartingIndex+ParentControl.ControlCount+1) > (Length(aAnchorStorage)) then
SetLength(aAnchorStorage, StartingIndex+ParentControl.ControlCount+1);
for iCounter := 0 to ParentControl.ControlCount - 1 do
begin
ChildControl := ParentControl.Controls[iCounter];
aAnchorStorage[StartingIndex] := ChildControl.Anchors;
//doesn't work for set of stacked top-aligned panels
// if ([akRight, akBottom ] * ChildControl.Anchors) <> [] then
// ChildControl.Anchors := [akLeft, akTop];
if (ChildControl.Anchors) <> [akTop, akLeft] then
ChildControl.Anchors := [akLeft, akTop];
// if ([akTop, akBottom] * ChildControl.Anchors) = [akTop, akBottom] then
// ChildControl.Anchors := ChildControl.Anchors - [akBottom];
Inc(StartingIndex);
end;
//Add children
for iCounter := 0 to ParentControl.ControlCount - 1 do
begin
ChildControl := ParentControl.Controls[iCounter];
if ChildControl is TWinControl then
DisableAnchors_Core(TWinControl(ChildControl), aAnchorStorage, StartingIndex);
end;
end;
Et les ancres étant réactivées de manière récursive:
procedure EnableAnchors(ParentControl: TWinControl; aAnchorStorage: TAnchorsArray);
var
StartingIndex: Integer;
begin
StartingIndex := 0;
EnableAnchors_Core(ParentControl, aAnchorStorage, StartingIndex);
end;
procedure EnableAnchors_Core(ParentControl: TWinControl; aAnchorStorage: TAnchorsArray; var StartingIndex: Integer);
var
iCounter: integer;
ChildControl: TControl;
begin
for iCounter := 0 to ParentControl.ControlCount - 1 do
begin
ChildControl := ParentControl.Controls[iCounter];
ChildControl.Anchors := aAnchorStorage[StartingIndex];
Inc(StartingIndex);
end;
//Restore children
for iCounter := 0 to ParentControl.ControlCount - 1 do
begin
ChildControl := ParentControl.Controls[iCounter];
if ChildControl is TWinControl then
EnableAnchors_Core(TWinControl(ChildControl), aAnchorStorage, StartingIndex);
end;
end;
Avec le travail de changer une police de contrôles laissée à:
procedure StandardizeFont_ControlFontCore(AControlFont: TFont; ForceClearType: Boolean;
FontName: string; FontSize: Integer;
ForceFontIfName: string; ForceFontIfSize: Integer);
const
CLEARTYPE_QUALITY = 5;
var
CanChangeName: Boolean;
CanChangeSize: Boolean;
lf: TLogFont;
begin
if not Assigned(AControlFont) then
Exit;
{$IFDEF ForceClearType}
ForceClearType := True;
{$ELSE}
if g_ForceClearType then
ForceClearType := True;
{$ENDIF}
//Standardize the font if it's currently
// "MS Shell Dlg 2" (meaning whoever it was opted into the 'change me' system
// "MS Sans Serif" (the Delphi default)
// "Tahoma" (when they wanted to match the OS, but "MS Shell Dlg 2" should have been used)
// "MS Shell Dlg" (the 9x name)
CanChangeName :=
(FontName <> '')
and
(AControlFont.Name <> FontName)
and
(
(
(ForceFontIfName <> '')
and
(AControlFont.Name = ForceFontIfName)
)
or
(
(ForceFontIfName = '')
and
(
(AControlFont.Name = 'MS Sans Serif') or
(AControlFont.Name = 'Tahoma') or
(AControlFont.Name = 'MS Shell Dlg 2') or
(AControlFont.Name = 'MS Shell Dlg')
)
)
);
CanChangeSize :=
(
//there is a font size
(FontSize <> 0)
and
(
//the font is at it's default size, or we're specifying what it's default size is
(AControlFont.Size = 8)
or
((ForceFontIfSize <> 0) and (AControlFont.Size = ForceFontIfSize))
)
and
//the font size (or height) is not equal
(
//negative for height (px)
((FontSize < 0) and (AControlFont.Height <> FontSize))
or
//positive for size (pt)
((FontSize > 0) and (AControlFont.Size <> FontSize))
)
and
//no point in using default font's size if they're not using the face
(
(AControlFont.Name = FontName)
or
CanChangeName
)
);
if CanChangeName or CanChangeSize or ForceClearType then
begin
if GetObject(AControlFont.Handle, SizeOf(TLogFont), @lf) <> 0 then
begin
//Change the font attributes and put it back
if CanChangeName then
StrPLCopy(Addr(lf.lfFaceName[0]), FontName, LF_FACESIZE);
if CanChangeSize then
lf.lfHeight := FontSize;
if ForceClearType then
lf.lfQuality := CLEARTYPE_QUALITY;
AControlFont.Handle := CreateFontIndirect(lf);
end
else
begin
if CanChangeName then
AControlFont.Name := FontName;
if CanChangeSize then
begin
if FontSize > 0 then
AControlFont.Size := FontSize
else if FontSize < 0 then
AControlFont.Height := FontSize;
end;
end;
end;
end;
C'est beaucoup plus de code que vous ne le pensiez; je sais. Ce qui est triste, c’est qu’il n’ya pas de développeur Delphi sur la Terre, à l’exception de moi, qui corrige leurs applications.
Cher développeur Delphi: Définissez votre police Windows sur Segoe UI 14pt, et corrigez votre application buggy
Note: Tout code est publié dans le domaine public. Aucune attribution requise.
Voici mon cadeau. Une fonction qui peut vous aider avec le positionnement horizontal des éléments dans vos présentations graphiques. Gratuit pour tous.
function CenterInParent(Place,NumberOfPlaces,ObjectWidth,ParentWidth,CropPercent: Integer): Integer;
{returns formated centered position of an object relative to parent.
Place - P order number of an object beeing centered
NumberOfPlaces - NOP total number of places available for object beeing centered
ObjectWidth - OW width of an object beeing centered
ParentWidth - PW width of an parent
CropPercent - CP percentage of safe margin on both sides which we want to omit from calculation
+-----------------------------------------------------+
| |
| +--------+ +---+ +--------+ |
| | | | | | | |
| +--------+ +---+ +--------+ |
| | | | | |
+-----------------------------------------------------+
| |<---------------------A----------------->| |
|<-C->|<------B----->|<-----B----->|<-----B---->|<-C->|
| |<-D>|
|<----------E------------>|
A = PW-C B = A/NOP C=(CP*PW)/100 D = (B-OW)/2
E = C+(P-1)*B+D }
var
A, B, C, D: Integer;
begin
C := Trunc((CropPercent*ParentWidth)/100);
A := ParentWidth - C;
B := Trunc(A/NumberOfPlaces);
D := Trunc((B-ObjectWidth)/2);
Result := C+(Place-1)*B+D;
end;