Jak sprawić, by mój GUI zachowywał się dobrze, gdy skalowanie czcionek systemu Windows jest większe niż 100%

Przy wyborze dużych rozmiarów czcionek w Panelu sterowania Windows (jak 125% lub 150%) występują problemy w aplikacji VCL, za każdym razem, gdy coś zostało ustawione pikselowo.

Weź TStatusBar.Panel. Ustawiłem jego szerokość tak, aby zawierał dokładnie jedną etykietę, teraz z dużymi czcionkami Etykieta "przepełnia". Ten sam problem z innymi komponentami.

Niektóre nowe laptopy od della wysyłają już z ustawieniem domyślnym 125%, więc o ile w przeszłości problem ten był dość rzadki, teraz jest naprawdę ważne.

Co można zrobić, aby przezwyciężyć ten problem?

Author: Charles, 2011-11-28

4 answers

Uwaga: proszę zobaczyć inne odpowiedzi, ponieważ zawierają bardzo cenne techniki. Moja odpowiedź tutaj zawiera tylko zastrzeżenia i przestrogi przed założeniem, że świadomość DPI jest łatwa.

Generalnie unikam skalowania DPI za pomocą TForm.Scaled = True. Świadomość DPI jest dla mnie ważna tylko wtedy, gdy staje się ważna dla klientów, którzy dzwonią do mnie i są gotowi za to zapłacić. Powodem technicznym tego punktu widzenia jest to, że DPI-świadomość czy nie, otwierasz okno na świat bólu. Wiele standardowych i kontrolki VCL innych firm nie działają dobrze w wysokiej rozdzielczości DPI. Zauważalny wyjątek, że części VCL, które zawijają typowe elementy sterujące Windows, działają wyjątkowo dobrze przy wysokiej rozdzielczości DPI. Ogromna liczba zewnętrznych i wbudowanych niestandardowych kontrolek Delphi VCL nie działa dobrze lub wcale przy wysokich DPI. Jeśli planujesz włączyć TForm.Skalowane upewnij się, że testujesz w rozdzielczości 96, 125 i 150 DPI dla każdego formularza w projekcie i każdej strony trzeciej oraz wbudowanej kontroli, której używasz.

Samo Delphi jest napisane w Delphi. Ma włączoną flagę high DPI awareness, dla większości form, chociaż nawet tak niedawno, jak w Delphi XE2, sami autorzy IDE zdecydowali się nie włączać tej flagi High DPI Awareness manifest. Zauważ, że w Delphi XE4 i późniejszych znacznik high DPI awareness jest włączony, a IDE wygląda dobrze.

Sugeruję, aby nie używać TForm.Scaled = true (co jest domyślne w Delphi, więc jeśli go nie zmodyfikowałeś, większość twoich formularzy ma Scaled = true) z FLAG High DPI Aware (jako pokazane w odpowiedziach Davida) z aplikacjami VCL, które są zbudowane przy użyciu wbudowanego delphi form designer.

Próbowałem w przeszłości zrobić minimalną próbkę tego rodzaju pęknięcia, którego można się spodziewać, gdy TForm.Skalowanie jest prawdziwe, a gdy skalowanie postaci Delphi ma usterkę. Te usterki nie zawsze są wyzwalane tylko przez wartość DPI inną niż 96. Nie udało mi się ustalić pełnej listy innych rzeczy, które zawierają zmiany rozmiaru czcionki w systemie Windows XP. Ale ponieważ większość z tych usterki pojawiają się tylko w moich własnych aplikacjach, w dość skomplikowanych sytuacjach, postanowiłem pokazać wam kilka dowodów, które możecie sami zweryfikować.

Delphi XE wygląda tak, gdy ustawisz skalowanie DPI na "Fonts @ 200%" W Windows 7, a Delphi XE2 jest podobnie zepsute w Windows 7 i 8, ale te usterki wydają się być naprawione od Delphi XE4:

Tutaj wpisz opis obrazka

Tutaj wpisz opis obrazka

Są to głównie standardowe kontrolki VCL, które źle zachowują się przy wysokim DPI. Zauważ, że większość rzeczy ma nie został w ogóle skalowany, więc programiści Delphi IDE zdecydowali się zignorować świadomość DPI, a także wyłączyć wirtualizację DPI. Ciekawy wybór.

Wyłącz wirtualizację DPI tylko wtedy, gdy chcesz to nowe dodatkowe źródło bólu i trudnych wyborów. Lepiej zostaw to w spokoju. Zauważ, że typowe kontrolki systemu Windows wydają się działać dobrze. Należy zauważyć, że kontrola Delphi data-explorer jest opakowaniem C # WinForms wokół standardowego drzewa Windows common control. To jest czysta usterka Microsoftu i jej naprawienie może wymagać od Embarcadero przepisania czystej natywnej kontroli drzewa. Net dla Eksploratora danych lub napisania kodu DPI-check-and-modify-properties, aby zmienić wysokość elementów w kontrolce. Nawet microsoft WinForms nie może obsłużyć wysokiej rozdzielczości DPI czysto, automatycznie i bez niestandardowego kodu kludge.

Update: interesujący factoid: podczas gdy Delphi IDE nie wydaje się być "zwirtualizowane", nie wykorzystuje zawartości manifestu pokazanej przez Davida do osiągnięcia "wirtualizacja bez DPI". Być może używa jakiejś funkcji API w czasie wykonywania.

Update 2: w odpowiedzi na to, jak chciałbym wspierać 100%/125% DPI, chciałbym wymyślić plan dwufazowy. Faza 1 polega na inwentaryzacji kodu dla niestandardowych kontrolek, które muszą być naprawione dla wysokich DPI, a następnie zaplanowaniu ich naprawy lub wycofania. Faza 2 będzie wziąć niektóre obszary mojego kodu, które są zaprojektowane jako formularze bez zarządzania układem i zmienić je na formularze, które używają pewnego rodzaju zarządzania układem aby zmiany DPI lub wysokości czcionki mogły działać bez przycinania. Podejrzewam, że ta praca układu " inter-control "byłaby znacznie bardziej złożona w większości aplikacji niż praca" intra-control".

Aktualizacja: w 2016 roku najnowsza wersja Delphi 10.1 Berlin działa dobrze na mojej stacji roboczej o rozdzielczości 150 dpi.

 49
Author: Warren P,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-23 19:23:03

Twoje ustawienia w .plik dfm będzie poprawnie skalowany, tak długo jak Scaled is True.

Jeśli ustawiasz wymiary w kodzie, musisz przeskalować je według Screen.PixelsPerInch podzielonego przez Form.PixelsPerInch. Użyj MulDiv, aby to zrobić.

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

To właśnie robi struktura trwałości formy, gdy Scaled jest True.

W rzeczywistości, można zrobić cogent argument dla zastąpienia tej funkcji z wersji, która ciężko koduje wartość 96 dla mianownika. Pozwala to na korzystanie z bezwzględne wartości wymiarów i nie martw się o zmianę znaczenia, jeśli zdarzy ci się zmienić skalowanie czcionek na komputerze programistycznym i ponownie zapisać .plik dfm. Ważne jest to, że PixelsPerInch właściwość przechowywana wplik dfm jest wartością maszyny, na którejplik dfm został ostatnio zapisany.

const
  SmallFontsPixelsPerInch = 96;

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

Więc, kontynuując temat, kolejną rzeczą, której należy się nie obawiać, jest to, że jeśli projekt jest rozwijany na wielu maszynach o różnych wartościach DPI, przekonasz się, że skalowanie, które Delphi używa podczas zapisywania .pliki DFM powodują, że kontrolki wędrują po serii edycji. W moim miejscu pracy, aby tego uniknąć, Mamy ścisłą politykę, że formularze są edytowane tylko w 96dpi (skalowanie 100%).

W rzeczywistości moja wersja ScaleFromSmallFontsDimension również uwzględnia możliwość, że czcionka formularza różni się w czasie wykonywania od tej ustawionej w czasie projektowania. Na maszynach XP moje formularze aplikacji używają 8pt Tahoma. Na Vista i up 9pt Segoe UI jest używany. Daje to jeszcze jeden stopień swobody. Na skalowanie musi to uwzględniać, ponieważ zakłada się, że bezwzględne wartości wymiarów użyte w kodzie źródłowym są względne względem linii bazowej 8pt Tahoma przy 96dpi.

Jeśli używasz żadnych obrazów lub glifów w interfejsie użytkownika, również te muszą być skalowane. Częstym przykładem mogą być glify używane na paskach narzędzi i menu. Glify te należy udostępnić jako zasoby ikon powiązane z plikiem wykonywalnym. Każda ikona powinna zawierać zakres rozmiarów, a następnie podczas wykonywania wybrać najbardziej odpowiedni rozmiar i załaduj go do listy obrazów. Niektóre szczegóły na ten temat można znaleźć tutaj: jak załadować ikony z zasobu bez aliasingu?

Innym przydatnym trikiem jest definiowanie wymiarów w jednostkach względnych, względem TextWidth lub TextHeight. Tak więc, jeśli chcesz, aby coś miało rozmiar około 10 pionowych linii, możesz użyć 10*Canvas.TextHeight('Ag'). Jest to bardzo szorstka i gotowa metryka, ponieważ nie pozwala na odstępy między wierszami i tak dalej. Jednak często wszystko, co musisz zrobić, to być w stanie zorganizować że GUI skaluje się poprawnie z PixelsPerInch.

Należy również oznaczyć aplikację jako świadomą wysokiej rozdzielczości DPI. Najlepszym sposobem na to jest poprzez manifest aplikacji. Ponieważ Delphi 's build tools don' t allow you to customize the manifest you use this force you to link your own manifest resource.

<?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>

Skrypt zasobów wygląda tak:

1 24 "Manifest.txt"

Gdzie Manifest.txt zawiera rzeczywisty manifest. Musisz również dołączyć sekcję comctl32 v6 i ustawić requestedExecutionLevel na asInvoker. Następnie łączysz ten skompilowany zasób z aplikacją i upewnij się, że Delphi nie próbuje zrobić tego samego z manifestem. We współczesnym Delphi można to osiągnąć, ustawiając opcję Runtime Themes project Na None.

Manifest jest prawo sposób zadeklarować swoją aplikację do wysokiej DPI świadomy. Jeśli chcesz po prostu szybko wypróbować, nie mieszając w manifeście, zadzwoń SetProcessDPIAware. Zrób to jako pierwsza rzecz, którą robisz, gdy aplikacja działa. Najlepiej w jednej z sekcji wczesnej inicjalizacji jednostki, lub jako pierwsza rzecz w Twoim .akta dpr.

Jeśli nie zadeklarujesz, że Twoja aplikacja jest świadoma wysokiej rozdzielczości DPI, Vista i up renderują ją w trybie legacy dla dowolnego skalowania czcionek powyżej 125%. To wygląda okropnie. Postaraj się nie wpaść w tę pułapkę.

Windows 8.1 na monitor DPI update

Od wersji Windows 8.1 istnieje teraz obsługa ustawień DPI dla każdego monitora ( http://msdn.microsoft.com/en-ca/magazine/dn574798.aspx ). jest to duży problem dla nowoczesnych urządzeń, które mogą mieć podłączone różne wyświetlacze o bardzo różnych możliwościach. Możesz mieć ekran laptopa o bardzo wysokiej rozdzielczości DPI i zewnętrzny projektor o niskiej rozdzielczości DPI. Wspieranie takiego scenariusza wymaga jeszcze więcej pracy niż opisano powyżej.

 60
Author: David Heffernan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:06

Ważne jest również, aby pamiętać, że honorowanie DPI użytkownika jest tylko podzbiorem Twojej prawdziwej pracy:]}

Uszanowanie rozmiaru czcionki użytkownika

Przez dziesięciolecia System Windows rozwiązywał ten problem z pojęciem wykonywania układu za pomocą jednostek dialogowych , a nie pikseli. A "Jednostka dialogowa" jest zdefiniowana tak, że średni znak czcionki wynosi

  • 4 jednostki dialogowe (dlus) wide i
  • 8 jednostek dialogowych (clus) wysoka

Tutaj wpisz opis obrazka

Delphi nie Statek z (buggy) pojęcie Scaled, gdzie formularz próbuje automatycznie dostosować na podstawie

  • ustawienia DPI systemu Windows użytkownika, wersety
  • W przeciwieństwie do innych formatów, nie można ich używać do tworzenia kopii zapasowych.]}

To nie rozwiązuje problemu, gdy użytkownik używa czcionki innej niż ta, z którą został zaprojektowany formularz, np.:]}

  • programista zaprojektował formularz z MS Sans Serif 8pt (gdzie średni znak to 6.21px x 13.00px, przy 96dpi)
  • Użytkownik uruchomiony z Tahoma 8pt (gdzie średni znak to 5.94px x 13.00px, przy 96dpi)

    Podobnie jak w przypadku każdego, kto tworzy aplikację dla Windows 2000 lub Windows XP.

Lub

    W 2004 roku, w ramach projektu, w ramach projektu, w ramach projektu, w 2006 roku, w ramach projektu, w 2007 roku, w 2009 roku, w 2009 roku, w 2009 roku, w 2009 roku, w 2009 roku, w 2009 roku, w 2009 roku]}
  • użytkownik uruchomiony z Segoe UI 9pt (gdzie średnia znak to 6.67px x 15px, przy 96dpi)

Jako dobry programista będziesz szanował preferencje czcionek swoich użytkowników. Oznacza to, że musisz również przeskalować wszystkie kontrolki w formularzu, aby dopasować nowy rozmiar czcionki: {]}

  • Rozwiń wszystko poziomo o 12.29% (6.67/5.94)
  • rozciągnij wszystko w pionie o 15.38% (15/13)

Scaled nie dam sobie z tym rady.

Jest gorzej, gdy:

  • zaprojektowałem twój formularz na Segoe UI 9pt (Windows Vista, Windows 7, Windows 8 domyślnie)
  • użytkownik uruchamia Segoe UI 14pt, (np. moje preferencje) czyli 10.52px x 25px

Teraz musisz wszystko skalować

  • poziomo o 57,72%
  • pionowo o 66,66%

Scaled nie dam sobie z tym rady.


Jeśli jesteś mądry, możesz zobaczyć, jak honorowanie DPI jest nierealne:

  • formularz zaprojektowany z Segoe UI 9pt @ 96dpi (6.67 px x 15px)
  • [[36]} user running with Segoe UI 9PT @ 150dpi (10.52 px x 25px)

Nie powinieneś patrzeć na ustawienia DPI użytkownika, powinieneś patrzeć na ich rozmiar czcionki. Dwóch użytkowników działa

  • Segoe UI 14pt @ 96dpi (10.52 px x 25px)
  • Segoe UI 9pt @ 150dpi (10.52 px x 25px)

uruchamiają tę samą czcionkę . DPI to tylko jeden rzecz, która wpływa na wielkość czcionki; preferencje użytkownika to inne.

Standaryzacjaformfont

Clovis zauważył, że odwołuję się do funkcji StandardizeFormFont, która naprawia czcionkę w formularzu i skaluje ją do nowego rozmiaru czcionki. Nie jest to standardowa funkcja, ale cały zbiór funkcji, które wykonują proste zadanie, którego Borland nigdy nie wykonał.

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 ma 6 różnych czcionek; nie ma jednego "ustawienia czcionki" w systemie Windows.
Z doświadczenia wiemy jednak, że nasze formularze powinny być zgodne z czcionką Icon Title Font ustawienie

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;

Gdy poznamy rozmiar czcionki, przeskalujemy formularz do , otrzymujemy bieżącą wysokość czcionki formularza (w pikselach ) i skalujemy w górę o ten współczynnik.

Na przykład, jeśli ustawiam formularz na -16, A formularz jest obecnie na -11, następnie musimy przeskalować całą formę przez:

-16 / -11 = 1.45454%

Standaryzacja przebiega w dwóch fazach. Najpierw przeskaluj formularz według proporcji nowych: starych rozmiarów czcionek. Wtedy faktycznie zmienić kontrolki (rekurencyjnie) do używania nowej czcionki.

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;

Oto zadanie rzeczywistego skalowania formularza. Działa wokół błędów w autorskiej metodzie Borland Form.ScaleBy. Najpierw należy wyłączyć wszystkie kotwice w formularzu, następnie wykonać skalowanie, a następnie ponownie włączyć kotwice: {]}

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;

I wtedy musimy rekurencyjnie użyć nowej czcionki:

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;

Z rekurencyjnie wyłączonymi kotwicami:

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;

I kotwice będące rekurencyjnie re-enabled:

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;

Z pracą polegającą na zmianie czcionki kontrolki w lewo na:

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;

To o wiele więcej kodu, niż myślałeś, że będzie; wiem. Smutne jest to, że na ziemi nie ma programisty Delphi, poza mną, który poprawi swoje aplikacje.

W tym celu należy ustawić czcionkę dla systemu Windows na Segoe UI 14pt i naprawić błędną aplikację

Uwaga : każdy kod jest wydany w domenie publicznej. Nie wymaga atrybucji.

 41
Author: Ian Boyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-15 04:06:16

Oto Mój dar. Funkcja, która może pomóc w poziomym pozycjonowaniu elementów w układach GUI. Za darmo dla wszystkich.

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
Author: avra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-29 13:40:28