Come faccio a comportarmi bene con la GUI quando il ridimensionamento dei font di Windows è superiore al 100%

Quando si scelgono le dimensioni di caratteri grandi nel pannello di controllo di Windows (come 125% o 150%), allora ci sono problemi in un’applicazione VCL, ogni volta che qualcosa è stato impostato in pixel.

Prendi il TStatusBar.Panel . Ho impostato la larghezza in modo che contenga esattamente un’etichetta, ora con caratteri grandi l’etichetta “straripa”. Lo stesso problema con altri componenti.

Alcuni nuovi portatili Dell hanno già il 125% come impostazione predefinita, quindi, mentre in passato questo problema era piuttosto raro, ora è davvero importante.

Cosa si può fare per superare questo problema?

Nota: vedere le altre risposte in quanto contengono tecniche molto preziose. La mia risposta qui fornisce solo avvertimenti e precauzioni contro l’assunzione di consapevolezza DPI è facile.

In genere evito il ridimensionamento DPI-aware con TForm.Scaled = True . La consapevolezza DPI è importante solo per me quando diventa importante per i clienti che mi chiamano e sono disposti a pagare per questo. La ragione tecnica dietro questo punto di vista è che la consapevolezza DPI o no, si sta aprendo una finestra in un mondo di dolore. Molti controlli VCL standard e di terze parti non funzionano bene con DPI alto. La notevole eccezione che le parti VCL che avvolgono i controlli comuni di Windows funzionano molto bene a DPI elevati. Un numero enorme di controlli personalizzati Delphi VCL di terze parti e non funzionano bene, o affatto, ad alto DPI. Se hai intenzione di triggersre TForm.Scaled assicurati di testare a 96, 125 e 150 DPI per ogni singolo modulo del progetto e ogni singolo terzo e controllo incorporato che usi.

Delphi stesso è scritto in Delphi. Ha triggersto il flag di consapevolezza DPI alto, per la maggior parte delle forms, anche se recentemente come in Delphi XE2, gli autori IDE hanno deciso di NON triggersre il flag manifest di Aid DPI. Si noti che in Delphi XE4 e versioni successive, il flag di consapevolezza HIGH DPI è triggersto e l’IDE sembra buono.

Ti suggerisco di non usare TForm.Scaled = true (che è un valore predefinito in Delphi quindi, a meno che tu non lo abbia modificato, la maggior parte dei tuoi moduli ha Scaled = true) con i flag A DPI alti (come mostrato nelle risposte di David) con Applicazioni VCL create utilizzando il designer di moduli delphi integrato.

In passato ho provato a fare un esempio minimo del tipo di rottura che ci si può aspettare quando TForm.Scaled è vero, e quando Delphi presenta un ridimensionamento ha un problema tecnico. Questi errori non sono sempre e sono triggersti ​​solo da un valore DPI diverso da 96. Non sono stato in grado di determinare un elenco completo di altre cose, incluse le modifiche alle dimensioni del carattere di Windows XP. Ma dal momento che la maggior parte di questi problemi si presentano solo nelle mie applicazioni, in situazioni abbastanza complesse, ho deciso di mostrarti alcune prove che puoi verificare.

Delphi XE assomiglia a questo quando si imposta il ridimensionamento DPI su “Caratteri @ 200%” in Windows 7, e Delphi XE2 è interrotto in modo simile su Windows 7 e 8, ma questi problemi sembrano risolti a partire da Delphi XE4:

inserisci la descrizione dell'immagine qui

inserisci la descrizione dell'immagine qui

Si tratta principalmente di controlli VCL standard che si comportano in modo anomalo a DPI elevato. Si noti che molte cose non sono state ridimensionate, quindi gli sviluppatori IDE Delphi hanno deciso di ignorare la consapevolezza DPI, oltre a distriggersre la virtualizzazione DPI. Una scelta così interessante.

Distriggers la virtualizzazione DPI solo se desideri questa nuova fonte aggiuntiva di dolore e le scelte difficili. Ti suggerisco di lasciar perdere. Si noti che i controlli comuni di Windows sembrano funzionare in modo ottimale. Si noti che il controllo Delphi Data-Explorer è un wrapper C # WinForms attorno a un controllo comune di Windows Tree standard. Questo è un glitch microsoft puro, e il suo fixing potrebbe richiedere a Embarcadero di riscrivere un puro controllo ad albero nativo .Net per il proprio data explorer, o di scrivere del codice DPI-check-and-modify-properties per modificare le altezze dell’object nel controllo. Nemmeno microsoft WinForms è in grado di gestire DPI elevati in modo pulito, automatico e senza codice kludge personalizzato.

Aggiornamento: Fattoide interessante: Mentre l’IDE delphi non sembra essere “virtualizzato”, non sta utilizzando il contenuto manifest mostrato da David per ottenere “virtualizzazione non DPI”. Forse sta usando alcune funzioni API in fase di runtime.

Aggiornamento 2: in risposta a come avrei supportato il 100% / 125% DPI, avrei elaborato un piano in due fasi. La fase 1 consiste nell’inventario del mio codice per i controlli personalizzati che devono essere corretti per DPI elevati e quindi fare un piano per risolverli o eliminarli. La fase 2 consisterebbe nel prendere alcune aree del mio codice che sono progettate come moduli senza la gestione del layout e convertirli in moduli che utilizzano una sorta di gestione del layout in modo che le modifiche all’altezza dei DPI o dei caratteri possano funzionare senza clipping. Sospetto che questo lavoro di “inter-controllo” sia molto più complesso nella maggior parte delle applicazioni rispetto al lavoro “intra-controllo”.

Aggiornamento: nel 2016, l’ultimo Delphi 10.1 Berlin funziona bene con la mia workstation a 150 dpi.

Le tue impostazioni nel file .dfm verranno ridimensionate correttamente, a condizione che Scaled sia True .

Se si impostano le dimensioni nel codice, è necessario ridimensionarle mediante Screen.PixelsPerInch diviso per Form.PixelsPerInch . Usa MulDiv per farlo.

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

Questo è ciò che fa il framework di persistenza dei form quando Scaled è True .

In effetti, puoi fare un ragionamento convincente per sostituire questa funzione con una versione che codifica hard un valore di 96 per il denominatore. Ciò consente di utilizzare valori di dimensione assoluta e di non preoccuparsi della modifica del significato se si modifica il ridimensionamento dei caratteri sul computer di sviluppo e di salvare nuovamente il file .dfm. La ragione che conta è che la proprietà PixelsPerInch memorizzata nel file .dfm sia il valore della macchina su cui è stato salvato l’ultimo file .dfm.

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

Quindi, continuando il tema, un’altra cosa da diffidare è che se il tuo progetto è sviluppato su più macchine con valori DPI differenti, troverai che il ridimensionamento che Delphi usa quando salva i file .dfm porta a controlli che vagano su una serie di modifiche . Al mio posto di lavoro, per evitare questo, abbiamo una politica rigorosa che i moduli sono sempre e solo modificati a 96 dpi (100% di ridimensionamento).

In effetti la mia versione di ScaleFromSmallFontsDimension anche della possibilità che il carattere del modulo differisca in fase di esecuzione da quello impostato in fase di progettazione. Sulle macchine XP i moduli della mia applicazione usano il Tahoma 8pt. Su Vista e su 9pt viene utilizzata l’interfaccia utente di Segoe. Questo fornisce ancora un altro grado di libertà. Il ridimensionamento deve tener conto di ciò poiché si assume che i valori di dimensione assoluta utilizzati nel codice sorgente siano relativi alla linea di base di 8pt Tahoma a 96 dpi.

Se si utilizzano immagini o glifi nell’interfaccia utente, è necessario ridimensionarli. Un esempio comune sono i glifi che vengono utilizzati su barre degli strumenti e menu. Ti consigliamo di fornire questi glifi come risorse di icone collegate al tuo eseguibile. Ogni icona dovrebbe contenere un intervallo di dimensioni e quindi in fase di esecuzione scegliere la dimensione più appropriata e caricarla in un elenco di immagini. Alcuni dettagli su questo argomento possono essere trovati qui: Come carico le icone da una risorsa senza subire l’aliasing?

Un altro trucco utile è definire le dimensioni in unità relative, rispetto a TextWidth o TextHeight . Quindi, se vuoi che qualcosa sia intorno alle 10 linee verticali puoi usare 10*Canvas.TextHeight('Ag') . Questa è una metrica molto approssimativa e pronta perché non consente l’interlinea e così via. Tuttavia, spesso tutto ciò che devi fare è essere in grado di organizzare che la GUI si PixelsPerInch correttamente con PixelsPerInch .

Dovresti anche contrassegnare la tua applicazione come ad alta conoscenza DPI . Il modo migliore per farlo è attraverso il manifest dell’applicazione. Poiché gli strumenti di sviluppo di Delphi non ti consentono di personalizzare il manifest che usi, questo ti costringe a colbind la tua risorsa manifest.

     true    

Lo script di risorse ha il seguente aspetto:

 1 24 "Manifest.txt" 

dove Manifest.txt contiene il manifest effettivo. Dovresti anche includere la sezione asInvoker comctl32 e impostare asInvoker su asInvoker . Quindi colleghi questa risorsa compilata alla tua app e assicurati che Delphi non tenti di fare lo stesso con il manifest. Nel moderno Delphi lo si ottiene impostando l’opzione del progetto Runtime Themes su None.

Il manifest è il modo giusto per dichiarare che la tua app è ad alta conoscenza DPI. Se vuoi semplicemente provarlo rapidamente senza fare casino con il tuo manifest, chiama SetProcessDPIAware . Fallo come la prima cosa che fai quando viene eseguita la tua app. Preferibilmente in una delle prime sezioni di inizializzazione dell’unità, o come prima cosa nel tuo file .dpr.

Se non si dichiara che l’app è ad alta definizione DPI, Vista e Up la renderanno in modalità legacy per qualsiasi ridimensionamento dei caratteri superiore al 125%. Questo sembra abbastanza terribile. Cerca di evitare di cadere in quella trappola.

Aggiornamento DPI 8.1 per monitor di Windows

A partire da Windows 8.1, ora è disponibile il supporto del sistema operativo per le impostazioni DPI per monitor ( http://msdn.microsoft.com/en-ca/magazine/dn574798.aspx ). Questo è un grosso problema per i dispositivi moderni che potrebbero avere schermi diversi collegati con funzionalità molto diverse. Potresti avere uno schermo per laptop DPI molto alto e un proiettore esterno a basso DPI. Supportare tale scenario richiede ancora più lavoro di quanto descritto sopra.

È anche importante notare che onorare il DPI dell’utente è solo un sottoinsieme del tuo vero lavoro:

onorare le dimensioni del carattere dell’utente

Per decenni, Windows ha risolto questo problema con la nozione di eseguire il layout utilizzando le unità di dialogo anziché i pixel. Una “unità di dialogo” è definita in modo che il carattere medio del carattere sia

  • 4 unità di dialogo (meno) larghe e
  • 8 unità di dialogo (clus) alte

inserisci la descrizione dell'immagine qui

Delphi viene spedito con una nozione di Scaled , in cui un modulo cerca di adattarsi automaticamente in base a

  • Impostazioni DPI di Windows dell’utente, versi
  • l’impostazione DPI sulla macchina dello sviluppatore che ha salvato per ultimo il modulo

Ciò non risolve il problema quando l’utente usa un carattere diverso da quello con cui hai progettato il modulo, ad esempio:

  • lo sviluppatore ha progettato il modulo con MS Sans Serif 8pt (dove il carattere medio è 6.21px x 13.00px , a 96 dpi)
  • utente in esecuzione con Tahoma 8pt (dove il carattere medio è 5.94px x 13.00px , a 96 dpi)

    Come nel caso di chiunque abbia sviluppato un’applicazione per Windows 2000 o Windows XP.

o

  • lo sviluppatore ha progettato il modulo con ** Tahoma 8pt * (dove il carattere medio è 5.94px x 13.00px , a 96 dpi)
  • un utente che esegue con Segoe UI 9pt (dove il carattere medio è 6.67px x 15px , a 96 dpi)

Come bravo sviluppatore, onorerai le preferenze dei caratteri dell’utente. Ciò significa che è necessario ridimensionare tutti i controlli del modulo in modo che corrispondano alle nuove dimensioni del carattere:

  • espandi tutto orizzontalmente del 12,29% (6,67 / 5,94)
  • allunga tutto verticalmente del 15,38% (15/13)

Scaled non gestirà questo per voi.

Peggiora quando:

  • progettato il tuo modulo su Segoe UI 9pt (Windows Vista, Windows 7, Windows 8 predefinito)
  • l’utente sta eseguendo Segoe UI 14pt , (ad es. la mia preferenza) che è 10.52px x 25px

Ora devi ridimensionare tutto

  • orizzontalmente del 57,72%
  • verticalmente del 66,66%

Scaled non gestirà questo per voi.


Se sei intelligente puoi vedere come onorare DPI è irrelavent:

  • modulo progettato con Segoe UI 9pt @ 96 dpi (6,67 px x 15 px)
  • utente che esegue con Segoe UI 9pt @ 150dpi (10.52px x 25px)

Non dovresti guardare le impostazioni DPI dell’utente, dovresti guardare le loro dimensioni dei caratteri . Due utenti in esecuzione

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

stanno facendo funzionare lo stesso carattere DPI è solo una cosa che influenza la dimensione del carattere; le preferenze dell’utente sono le altre.

StandardizeFormFont

Clovis ha notato che faccio riferimento a una funzione StandardizeFormFont che corregge il carattere su un modulo e la ridimensiona alla nuova dimensione del carattere. Non è una funzione standard, ma un’intera serie di funzioni che svolgono il compito semplice che Borland non ha mai gestito.

 function StandardizeFormFont(AForm: TForm): Real; var preferredFontName: string; preferredFontHeight: Integer; begin GetUserFontPreference({out}preferredFontName, {out}preferredFontHeight); //eg "Segoe UI", Result := Toolkit.StandardizeFormFont(AForm, PreferredFontName, PreferredFontHeight); end; 

Windows ha 6 tipi di carattere diversi; non c’è una singola “impostazione dei caratteri” in Windows.
Ma sappiamo per esperienza che le nostre forms dovrebbero seguire l’impostazione Font del titolo dell’icona

 procedure GetUserFontPreference(out FaceName: string; out PixelHeight: Integer); var font: TFont; begin font := Toolkit.GetIconTitleFont; try FaceName := font.Name; //eg "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; //eg -16 finally font.Free; end; end; 

Una volta che conosciamo le dimensioni del carattere in cui ridimensioneremo il modulo, otteniamo l’altezza del carattere corrente del modulo ( in pixel ) e aumentiamo di livello in base a quel fattore.

Ad esempio, se sto impostando il modulo su -16 e il modulo è attualmente su -11 , allora dobbiamo ridimensionare l’intero modulo per:

 -16 / -11 = 1.45454% 

La standardizzazione avviene in due fasi. Innanzitutto ridimensiona la forma in base al rapporto tra le nuove dimensioni dei caratteri precedenti. Quindi modificare effettivamente i controlli (in modo ricorsivo) per utilizzare il nuovo carattere.

 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 (eg 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; 

Ecco il compito di ridimensionare effettivamente un modulo. Funziona attorno ai bug nel metodo Form.ScaleBy Borland. Prima deve disabilitare tutti gli ancoraggi nel modulo, quindi eseguire il ridimensionamento, quindi riabilitare gli ancoraggi:

 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; 

e quindi dobbiamo ricorsivamente effettivamente utilizzare il nuovo carattere:

 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; 

Con gli ancoraggi disabilitati ricorsivamente:

 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; 

E le ancore vengono ricorsivamente riabilitate:

 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; 

Con il lavoro di modificare effettivamente un carattere di controllo rimasto a:

 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; 

È molto più codice di quanto pensavi che sarebbe stato; lo so. La cosa triste è che non c'è nessuno sviluppatore Delphi al mondo, tranne me, che in realtà rende le loro applicazioni corrette.

Gentile sviluppatore Delphi : imposta il tuo font Windows su Segoe UI 14pt e aggiusta la tua applicazione buggy

Nota : qualsiasi codice è rilasciato nel pubblico dominio. Nessuna attribuzione richiesta.

Ecco il mio regalo Una funzione che può aiutarti con il posizionamento orizzontale degli elementi nei tuoi layout della GUI. Gratuito per tutti

 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;