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?
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:
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.
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.
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
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.
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;
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