Comment faire pour que mon interface graphique se comporte bien lorsque la mise à l'échelle de la police de Windows est supérieure à 100%

lors du choix de grandes tailles de police dans le panneau de configuration de Windows (comme 125%, ou 150%), alors il y a des problèmes dans une application VCL, chaque fois que quelque chose a été mis pixelwise.

prenez le TStatusBar.Panel . J'ai paramétré sa largeur de manière à ce qu'elle contienne exactement une étiquette, maintenant avec de grandes polices l'étiquette "overflows". Même problème avec les autres composants.

quelques nouveaux ordinateurs portables de Dell ship déjà avec 125% comme paramètre par défaut, alors que dans le passé ce le problème était assez rare maintenant, il est vraiment important.

que peut-on faire pour surmonter ce problème?

99
demandé sur Charles 2011-11-28 17:51:33

4 réponses

Note: veuillez consulter les autres réponses car elles contiennent des techniques très utiles. Ma réponse ici ne fournit que des mises en garde et des mises en garde contre le fait de supposer que L'on a facilement conscience de l'existence de L'IPD.

j'en général d'éviter les DPI-courant de mise à l'échelle avec TForm.Scaled = True . La sensibilisation à L'IAD n'est importante pour moi que lorsqu'elle devient importante pour les clients qui m'appellent et qui sont prêts à payer pour cela. La raison technique derrière ce point de vue est que DPI-conscience ou pas, vous ouvrez une fenêtre dans un monde de souffrance. De nombreux contrôles VCL standard et tiers ne fonctionnent pas bien dans les DPI élevés. L'exception notable que les pièces VCL qui enveloppent les commandes communes de fenêtres fonctionnent remarquablement bien à haute DPI. Un grand nombre de tiers et de contrôles personnalisés Delphi VCL intégrés ne fonctionnent pas bien, ou pas du tout, à des DPI élevés. Si vous prévoyez d'allumer TForm.Scaled assurez-vous de tester à 96, 125, et 150 DPI pour chaque forme simple dans votre projet, et chaque tiers et construit dans le contrôle que vous utilisez.

Delphi lui-même est écrit en Delphi. Il a le haut drapeau de conscience DPI allumé, pour la plupart des formes, bien que, même aussi récemment que dans Delphi XE2, les auteurs IDE eux-mêmes ont décidé de ne pas tourner ce haut drapeau de conscience DPI manifeste. Notez que dans Delphi XE4 et plus tard, le drapeau de haute sensibilisation DPI est activé, et l'IDE semble bon.

je vous conseille de ne pas utiliser TForm.Scaled=true (qui est un défaut dans Delphi donc à moins que vous ne l'ayez modifié, la plupart de vos formulaires ont Scaled=true) avec les drapeaux high DPI Aware (comme indiqué dans les réponses de David) avec les applications VCL qui sont construits en utilisant le Delphi Form designer intégré.

j'ai essayé dans le passé de faire un échantillon minimal du genre de bris que vous pouvez vous attendre à voir quand TForm.Scaled est vrai, et quand Delphi forme scaling a un bug. Ces problèmes ne sont pas toujours et seulement déclenchés par une valeur DPI autre que 96. J'ai été incapable de déterminer une liste complète d'autres choses, qui inclut les changements de taille de police de Windows XP. Mais comme la plupart de ces problèmes n'apparaissent 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êmes.

Delphi XE ressemble à cela lorsque vous définissez la mise à L'échelle DPI à " polices @ 200%" dans Windows 7, et Delphi XE2 est également cassé sur Windows 7 et 8, mais ces problèmes semblent être fixés à partir de Delphi XE4:

enter image description here

enter image description here

il s'agit principalement de contrôles VCL Standard qui se comportent mal à haute DPI. Notez que la plupart des choses n'ont pas été mises à l'échelle du tout, de sorte que les développeurs Delphi IDE ont décidé d'ignorer la prise de conscience du DPI, ainsi que de désactiver la virtualisation du DPI. Un choix si intéressant.

désactivez la virtualisation DPI seulement si vous voulez cette nouvelle source supplémentaire de douleur, et des choix difficiles. Je vous suggérons de le laisser seul. Notez que les commandes communes de Windows semblent fonctionner très bien. Notez que le contrôle Delphi data-explorer est un enveloppeur C# WinForms autour d'un contrôle standard de Windows Tree. C'est un bug de microsoft pur, et le fixer pourrait nécessiter soit L'Embarradero pour réécrire un pur contrôle d'arbre natif .Net pour leur Explorateur de données, ou pour écrire un code de propriétés DPI-check-and-modify-pour changer les hauteurs d'item dans le contrôle. Même microsoft WinForms ne peut pas gérer des DPI élevés proprement, automatiquement et sans code personnalisé kludge.

mise à Jour: légende Intéressante: Alors que le delphi IDE ne semble pas être "virtualisé", il n'est pas en utilisant le contenu manifeste illustré par David pour atteindre le "non-DPI-virtualisation". Peut-être utilise-t-il une fonction API à l'exécution.

mise à jour 2: en réponse à la façon dont je soutiendrais 100%/125% DPI, je proposerais un plan en deux phases. Phase 1 est d'inventorier mon code pour les contrôles personnalisés qui doivent être fixes de haute résolution, et ensuite faire un plan pour les corriger ou les supprimer. La Phase 2 serait de prendre certaines parties de mon code qui sont conçues comme des formes sans gestion de la mise en page et de les remplacer par des formes qui utilisent une sorte de gestion de la mise en page afin que les changements de DPI ou de hauteur de la police puissent fonctionner sans découpage. Je soupçonne 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 En 2016, Le Dernier Delphi 10.1 Berlin fonctionne bien sur mon poste de travail de 150 dpi.

49
répondu Warren P 2016-05-23 19:23:03

Vos paramètres dans le .dfm fichier sera mis à l'échelle correctement, tant que Scaled est True .

si vous définissez des dimensions dans le code, vous devez les mettre à l'échelle par Screen.PixelsPerInch divisé par Form.PixelsPerInch . Utilisez MulDiv pour ce faire.

function TMyForm.ScaleDimension(const X: Integer): Integer;
begin
  Result := MulDiv(X, Screen.PixelsPerInch, PixelsPerInch);
end;

C'est ce que fait la forme persistence framework lorsque Scaled est True .

en fait, vous pouvez faire argument convaincant pour remplacer cette fonction par une version qui Code dur une valeur de 96 pour le dénominateur. Cela vous permet d'utiliser des valeurs absolues de dimension et de ne pas vous soucier du changement de signification si vous changez la mise à l'échelle de la police sur votre machine de développement et de sauvegarder à nouveau le .dfm fichier. La raison qui importe est que la propriété PixelsPerInch stocké dans le .dfm fichier est la valeur de la machine sur laquelle l' .dfm fichier a été enregistré.

const
  SmallFontsPixelsPerInch = 96;

function ScaleFromSmallFontsDimension(const X: Integer): Integer;
begin
  Result := MulDiv(X, Screen.PixelsPerInch, SmallFontsPixelsPerInch);
end;

So, en continuant le thème, une autre chose dont il faut se méfier est que si votre projet est développé sur plusieurs machines avec des valeurs différentes de DPI, vous trouverez que la mise à l'échelle que Delphi utilise lors de la sauvegarde .les fichiers dfm donnent des contrôles errant au-dessus d'une série d'éditions. Sur mon lieu de travail, pour éviter cela, nous avons une politique stricte selon laquelle les formulaires ne sont édités qu'à 96 DPI (100% de mise à l'échelle).

en fait ma version de ScaleFromSmallFontsDimension tient également compte de la possibilité de la forme font différant à l'exécution de celui défini à designtime. Sur les machines XP les formulaires de ma demande utilisent 8pt Tahoma. Sur Vista et up 9pt Segoe UI est utilisé. Cela fournit encore un autre degré de liberté. La mise à l'échelle doit tenir compte de cela parce que les valeurs absolues des dimensions utilisées dans le code source sont supposées être relatives à la ligne de base de 8pt Tahoma à 96 dpi.

Si vous utilisez des images ou des glyphes dans votre INTERFACE de ces besoins à l'échelle. Un bon exemple serait les glyphes sont utilisés dans les barres d'outils et les menus. Vous voudrez fournir ces glyphes comme ressources d'icône liées à votre exécutable. Chaque icône devrait contenir une gamme de tailles et ensuite à l'exécution vous choisissez la taille la plus appropriée et la chargez dans une liste d'image. Certains détails sur ce sujet peuvent être trouvés ici: Comment puis-je charger des icônes à partir d'une ressource sans souffrir de l'alias?

un autre truc utile est de définir les dimensions en unités relatives, par rapport à TextWidth ou TextHeight . Donc, si vous voulez que quelque chose soit autour de 10 lignes verticales dans la taille, vous pouvez utiliser 10*Canvas.TextHeight('Ag') . C'est très approximatif et prêt métrique car elle ne permet pas pour l'espacement des lignes et ainsi de suite. Cependant, souvent tout ce que vous devez faire est d'être en mesure d'organiser que les échelles de L'interface graphique correctement avec PixelsPerInch .

, Vous devez également marquer votre application comme étant haute résolution . La meilleure façon de le faire est par l'application manifester. Puisque les outils de construction de Delphi ne vous permettent pas de personnaliser le manifeste que vous utilisez, cela vous force à 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 ressource ressemble à ceci:

1 24 "Manifest.txt"

Manifest.txt contient le manifeste. Vous devez également inclure la section comctl32 v6 et mettre requestedExecutionLevel à asInvoker . Vous liez ensuite cette ressource compilée à votre application et assurez-vous que Delphi n'essaie pas faire de même avec son manifeste. Dans Delphi moderne, vous réalisez cela en mettant L'option Runtime Themes project à zéro.

le manifeste est le droit façon de déclarer votre application pour être élevé DPI conscient. Si vous voulez juste l'essayer rapidement sans toucher à votre manifeste, appelez SetProcessDPIAware . Faites - le comme la première chose que vous faites lorsque votre application fonctionne. De préférence dans l'une des premières sections d'initialisation de l'unité, ou la première chose dans votre .rmr de fichier.

si vous ne déclarez pas que votre application est au courant des DPI élevés alors Vista et up la rendra en mode legacy pour toute mise à l'échelle de police supérieure à 125%. Cela ressemble tout à fait redoutable. Essayez d'éviter de tomber dans ce piège.

Windows 8.1 par moniteur DPI update

à partir de Windows 8.1, il y a maintenant un support OS pour les paramètres DPI par moniteur ( http://msdn.microsoft.com/en-ca/magazine/dn574798.aspx ). C'est un gros problème pour les appareils modernes, qui peuvent être différentes d'affiche jointe avec différentes capacités. Vous pourriez avoir un écran D'ordinateur portable DPI très élevé, et un projecteur externe DPI faible. Soutenir un tel scénario prend encore plus de travail que décrit ci-dessus.

60
répondu David Heffernan 2017-05-23 12:10:06

il est également important de noter qu'honorer le DPI de l'utilisateur n'est qu'un sous-ensemble de votre vrai travail:

honorer la taille de police de l'utilisateur

depuis des décennies, Windows a résolu ce problème avec la notion d'exécution de la disposition en utilisant Unités de dialogue , plutôt que des pixels. Un dialogue "unité" est définie de sorte que la police moyenne "caractère 1519310920" est

  • 4 unités de dialogue (dlus) large, et
  • 8 unités de dialogue (clus) haut

enter image description here

Delphi ne sont livrés avec un (buggy) notion de Scaled , où un formulaire essaie de régler automatiquement, basée sur le

  • Windows paramètres PPP de l'utilisateur, versets
  • le réglage DPI sur la machine de la développeur qui a sauvegardé la dernière fois le formulaire

qui 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:

  • développeur conçu le formulaire avec MS Sans Serif 8pt (où le caractère moyen est 6.21px x 13.00px , à 96dpi)
  • utilisateur tournant avec Tahoma 8pt (où le caractère moyen est 5.94px x 13.00px , à 96 dpi)

    comme c'était le cas pour quiconque développait une application Pour Windows 2000 ou Windows XP.

ou

  • développeur conçu la forme avec **Tahoma 8pt* (où la moyenne de caractère est 5.94px x 13.00px , à 96 ppp)
  • un utilisateur tournant avec Segoe UI 9pt (où le caractère moyen est 6.67px x 15px , à 96 dpi)

en tant que bon développeur, vous allez honorer les préférences de police de votre utilisateur. Cela signifie que vous devez également mettre à l'échelle toutes les commandes sur votre formulaire pour correspondre à la nouvelle taille de police:

  • étendre tout horizontalement de 12.29% (6.67/5.94)
  • tout étirer verticalement de 15,38% (15/13)

Scaled ne gérera pas cela pour vous.

It aggravation lorsque:

  • conçu votre formulaire à Segoe UI 9pt (Windows Vista, Windows 7, Windows 8 par défaut)
  • utilisateur est en cours d'exécution Segoe UI 14pt , (par exemple, ma préférence) qui est 10.52px x 25px

Maintenant que vous avez à l'échelle de tout

  • horizontalement de 57,72%
  • verticalement par 66.66%

Scaled ne gérera pas cela pour vous.


si vous êtes intelligent, vous pouvez voir à quel point honorer DPI est irrelavent:

  • forme conçue avec Segoe UI 9pt @ 96dpi (6,67 px x 15px)
  • user running with Segoe UI 9pt @ 150dpi (10.52 px x 25px)

Vous ne devriez pas être à la recherche de l'utilisateur configuration DPI, vous devriez être en en regardant leur taille de police . Deux utilisateurs tournant

  • Segoe UI 14pt @ 96dpi (10,52 px x 25px)
  • Segoe UI 9pt @ 150dpi (10,52 px x 25px)

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 les autres.

Normaliserleformfont

Clovis a remarqué que je me réfère à une fonction StandardizeFormFont qui fixe la police sur un formulaire, et la balance à la nouvelle taille de police. Ce n'est pas une fonction standard, mais un ensemble complet 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 de caractères différentes; il n'y a pas un seul "réglage de police" dans Windows.

Mais nous savons par expérience que nos formulaires suivre le police de titre D'icône paramètre

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 connaîtrons la taille de la police , nous passerons de la forme à , nous obtiendrons la hauteur actuelle de la police de la forme ( en pixels ), et nous augmenterons de ce facteur.

par exemple, si je mets le formulaire à -16 , et le formulaire est actuellement à -11 , alors nous avons besoin

-16 / -11 = 1.45454%

la standardisation se fait en deux phases. La première échelle de la forme par le rapport des nouvelles: anciennes tailles de police. Ensuite, changez les commandes (récursivement) 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;

c'est le travail de mettre à l'échelle une forme. Il fonctionne autour des bugs dans la méthode Form.ScaleBy de Borland. Il faut d'abord désactiver toutes les ancres sur la forme, puis exécuter 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 récursivement réellement utiliser 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 façon 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 façon 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 réellement changer une police de contrôle gauche à:

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 toi pensais que ça allait être; je le sais. La chose triste est qu'il n'y a pas de développeur Delphi sur terre, sauf pour moi, qui fait réellement leurs applications correctes.

Dear Delphi Developer : Réglez votre police Windows sur Segoe UI 14pt , et fixez votre application buggy

Note : tout code est publié dans le domaine public. Aucune attribution n'est requise.

41
répondu Ian Boyd 2016-01-15 04:06:16

Voici mon cadeau. Une fonction qui peut vous aider avec le positionnement horizontal des éléments dans vos configurations GUI. 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;
11
répondu avra 2011-11-29 13:40:28