Windows yazı tipi ölçeklendirmesi% 100'den büyük olduğunda GUI'min iyi davranmasını nasıl sağlayabilirim


108

Windows kontrol panelinde büyük yazı tipi boyutları seçerken (% 125 veya% 150 gibi), bir VCL uygulamasında bir şey piksel şeklinde her ayarlandığında sorunlar ortaya çıkar.

Alın TStatusBar.Panel. Genişliğini tam olarak bir etiket içerecek şekilde ayarladım, şimdi büyük yazı tipleriyle etiket "taşıyor". Diğer bileşenlerle aynı sorun.

Dell'den bazı yeni dizüstü bilgisayarlar zaten varsayılan ayar olarak% 125 ile geliyor, bu nedenle geçmişte bu sorun oldukça nadir iken şimdi gerçekten önemli.

Bu sorunun üstesinden gelmek için ne yapılabilir?

Yanıtlar:


56

Not: Çok değerli teknikler içerdikleri için lütfen diğer cevaplara bakınız. Buradaki cevabım yalnızca DPI farkındalığının kolay olduğunu varsaymaya karşı uyarılar ve uyarılar sağlar.

Genellikle ile DPI farkında ölçeklendirme önlemek TForm.Scaled = True. DPI bilinci benim için yalnızca beni arayan ve bunun için para ödemeye istekli müşteriler için önemli olduğunda önemlidir. Bu bakış açısının arkasındaki teknik neden, DPI farkındalığının olsun ya da olmasın, acı dolu bir dünyaya bir pencere açmanızdır. Birçok standart ve üçüncü taraf VCL denetimi, Yüksek DPI'da iyi çalışmaz. Windows Ortak Denetimleri'ni saran VCL parçalarının yüksek DPI'da oldukça iyi çalışması dikkate değer istisnadır. Çok sayıda üçüncü taraf ve yerleşik Delphi VCL özel kontrolleri, yüksek DPI'da iyi çalışmaz veya hiç çalışmaz. TForm.Scaled'i açmayı planlıyorsanız, projenizdeki her bir form ve kullandığınız her üçüncü taraf ve yerleşik denetim için 96, 125 ve 150 DPI'da test ettiğinizden emin olun.

Delphi'nin kendisi Delphi'de yazılmıştır. Çoğu form için Yüksek DPI farkındalık bayrağı açık olsa da, Delphi XE2'de olduğu gibi yakın zamanda bile, IDE yazarlarının kendileri, Yüksek DPI Farkındalık bildirimi bayrağını AÇMAMAYA karar verdiler. Delphi XE4 ve sonraki sürümlerinde YÜKSEK DPI farkındalık bayrağının açık olduğunu ve IDE'nin iyi göründüğünü unutmayın.

Yüksek DPI Duyarlı bayrakları (David'in yanıtlarında gösterildiği gibi) ile TForm.Scaled = true (bu, Delphi'de bir varsayılandır, bu nedenle değiştirmediğiniz sürece formlarınızın çoğunda Scaled = true) kullanmamanızı öneririm. Yerleşik delphi form tasarımcısı kullanılarak oluşturulan VCL uygulamaları.

Geçmişte, TForm.Scaled doğru olduğunda ve Delphi form ölçeklemesinde bir aksaklık olduğunda görmeyi bekleyebileceğiniz kırılma türünün minimum bir örneğini yapmaya çalıştım. Bu aksaklıklar her zaman ve yalnızca 96 dışında bir DPI değeriyle tetiklenmez. Windows XP yazı tipi boyutu değişikliklerini içeren diğer şeylerin tam bir listesini belirleyemedim. Ancak bu hataların çoğu yalnızca kendi uygulamalarımda ortaya çıktığı için, oldukça karmaşık durumlarda, kendinizi doğrulayabileceğiniz bazı kanıtlar göstermeye karar verdim.

Windows 7'de DPI Ölçeklendirmeyi "% 200'de Yazı Tipleri" olarak ayarladığınızda Delphi XE şöyle görünür ve Delphi XE2, Windows 7 ve 8'de benzer şekilde bozulur, ancak bu aksaklıklar Delphi XE4'ten itibaren düzeltilmiş görünmektedir:

görüntü açıklamasını buraya girin

görüntü açıklamasını buraya girin

Bunlar çoğunlukla yüksek DPI'da hatalı davranan Standart VCL denetimleridir. Çoğu şeyin ölçeklenmediğini unutmayın, bu nedenle Delphi IDE geliştiricileri DPI farkındalığını görmezden gelmeye ve DPI sanallaştırmayı kapatmaya karar verdiler. Ne kadar ilginç bir seçim.

DPI sanallaştırmayı yalnızca bu yeni ek acı kaynağını ve zor seçimleri istiyorsanız kapatın. Onu rahat bırakmanı öneririm. Windows ortak denetimlerinin çoğunlukla iyi çalıştığını unutmayın. Delphi veri gezgini denetiminin, standart bir Windows Ağacı ortak denetimi etrafında bir C # WinForms sarmalayıcısı olduğunu unutmayın. Bu tamamen bir microsoft aksaklığıdır ve düzeltilmesi, Embarcadero'nun veri gezgini için saf bir yerel .Net ağacı kontrolünü yeniden yazmasını veya kontroldeki öğe yüksekliklerini değiştirmek için bazı DPI-kontrol et ve değiştir-özellikler kodu yazmasını gerektirebilir. Microsoft WinForms bile yüksek DPI'yı temiz, otomatik ve özel kludge kodu olmadan işleyemez.

Güncelleme: İlginç gerçekler: delphi IDE "sanallaştırılmış" görünmese de, "DPI dışı sanallaştırma" elde etmek için David tarafından gösterilen manifest içeriği kullanmıyor. Belki çalışma zamanında bazı API işlevlerini kullanıyor.

Güncelleme 2:% 100 /% 125 DPI'ı nasıl destekleyeceğime yanıt olarak, iki aşamalı bir plan hazırlardım. Aşama 1, yüksek DPI için düzeltilmesi gereken özel kontroller için kodumun envanterini çıkarmak ve ardından bunları düzeltmek veya aşamalı olarak kaldırmak için bir plan yapmaktır. Aşama 2, kodumun düzen yönetimi olmadan formlar olarak tasarlanmış bazı alanlarını alıp bunları, DPI veya yazı tipi yüksekliği değişikliklerinin kırpılmadan çalışabilmesi için bir tür düzen yönetimi kullanan formlara dönüştürmektir. Bu "kontroller arası" düzen çalışmasının çoğu uygulamada "kontrol içi" çalışmadan çok daha karmaşık olacağından şüpheleniyorum.

Güncelleme: 2016'da, en son Delphi 10.1 Berlin, 150 dpi iş istasyonumda iyi çalışıyor.


5
Bu API işlevi olacaktır SetProcessDPIAware.
David Heffernan

2
Mükemmel. Yeni factoid için teşekkürler. Bunu olası bir yol olarak önermek için cevabınızı değiştirmenizi öneririm. Müşteriler bu seçeneği yapılandırmak bile isteyebilir (kendileri için işe yaramazsa kapatın).
Warren P

Delphi'nin açılış ekranı DPI Sanallaştırmayı kullanır, bunun nedeni muhtemelen SetDPIAware'e yapılan çağrı Açılış formu zaten görünür hale getirildikten sonra olmasıdır.
Warren P

6
RAD Studio, standart VCL kontrolleri, özel kontroller, .NET WinForms ve FireMonkey formlarının büyük bir karışımıdır. Sorunların olması şaşırtıcı değil. İşte bu nedenle RAD Studio iyi bir örnek değildir.
Torbins

1
Haklıysanız, başı kumun içinde olan VCL'nin kendisidir. Microsoft'un bile kafası kumda. Şimdiye kadar kullandığım, bu konuda uzaktan geçilebilir bir iş yapan tek çerçeve, Mac'teki COCOA.
Warren P

64

Dfm dosyasında ayarlarınız müddetçe, doğru ölçekli olacak Scaledolan True.

Boyutları kodda ayarlıyorsanız, onları Screen.PixelsPerInchbölerek ölçeklemeniz gerekir Form.PixelsPerInch. MulDivBunu yapmak için kullanın .

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

Bu, form kalıcılığı çerçevesinin ne zaman Scaledyaptığıdır True.

Aslında, bu işlevi payda için 96 değerini sabit kodlayan bir sürümle değiştirmek için ikna edici bir argüman oluşturabilirsiniz. Bu, mutlak boyut değerlerini kullanmanıza ve geliştirme makinenizde yazı tipi ölçeklemesini değiştirirseniz ve .dfm dosyasını yeniden kaydederseniz, anlamın değişmesi konusunda endişelenmenize izin vermez. Önemli olan neden PixelsPerInch, .dfm dosyasında depolanan özelliğin, .dfm dosyasının en son kaydedildiği makinenin değeri olmasıdır.

const
  SmallFontsPixelsPerInch = 96;

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

Bu nedenle, temayı sürdürmek, dikkat etmeniz gereken başka bir şey, projeniz farklı DPI değerlerine sahip birden çok makinede geliştirildiyse, Delphi'nin .dfm dosyalarını kaydederken kullandığı ölçeklendirmenin, bir dizi düzenleme üzerinde dolaşan kontrollerle sonuçlandığını göreceksiniz. . İş yerimde, bundan kaçınmak için, formların yalnızca 96dpi'de (% 100 ölçeklendirme) düzenlendiği katı bir politikamız var.

Aslında benim ScaleFromSmallFontsDimensionsürümüm, çalışma zamanında, tasarım zamanında ayarlanandan farklı olan form fontu olasılığını da hesaba katıyor. XP makinelerinde uygulamamın formları 8pt Tahoma kullanıyor. Vista ve üzeri 9pt Segoe UI kullanılır. Bu, başka bir derece özgürlük sağlar. Ölçeklendirme bunu hesaba katmalıdır çünkü kaynak kodda kullanılan mutlak boyut değerlerinin, 96dpi'de 8pt Tahoma'nın taban çizgisine göre olduğu varsayılır.

Kullanıcı arayüzünüzde herhangi bir resim veya glif kullanırsanız, bunların da ölçeklenmesi gerekir. Yaygın bir örnek, araç çubuklarında ve menülerde kullanılan gliflerdir. Bu glifleri, yürütülebilir dosyanıza bağlı simge kaynakları olarak sağlamak isteyeceksiniz. Her simge bir dizi boyut içermelidir ve daha sonra çalışma zamanında en uygun boyutu seçip bir görüntü listesine yüklersiniz. Bu konuyla ilgili bazı ayrıntılar burada bulunabilir: Örtüşme sorunu olmadan bir kaynaktan nasıl simge yüklerim?

Bir başka kullanışlı numara da boyutları göreceli birimlerle TextWidthveya ile ilişkili olarak tanımlamaktır TextHeight. Yani, bir şeyin yaklaşık 10 dikey çizgi boyutunda olmasını istiyorsanız kullanabilirsiniz 10*Canvas.TextHeight('Ag'). Bu çok kaba ve hazır bir metriktir çünkü satır aralığına vb. İzin vermez. Bununla birlikte, çoğu zaman yapmanız gereken tek şey, GUI'nin doğru şekilde ölçeklenmesini ayarlayabilmektir PixelsPerInch.

Ayrıca uygulamanızı yüksek DPI duyarlı olarak işaretlemelisiniz . Bunu yapmanın en iyi yolu, uygulama bildirimidir. Delphi'nin oluşturma araçları, kullandığınız bildirimi özelleştirmenize izin vermediğinden, bu sizi kendi bildirim kaynağınızı bağlamaya zorlar.

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

Kaynak komut dosyası şöyle görünür:

1 24 "Manifest.txt"

nerede Manifest.txtfiili liste içeren. Ayrıca comctl32 v6 bölümünü dahil etmeniz ve olarak ayarlamanız requestedExecutionLevelgerekir asInvoker. Daha sonra bu derlenmiş kaynağı uygulamanıza bağlarsınız ve Delphi'nin manifestiyle aynı şeyi yapmaya çalışmadığından emin olun. Modern Delphi'de bunu, Runtime Themes proje seçeneğini Yok olarak ayarlayarak elde edersiniz.

Manifest, uygulamanızı yüksek DPI duyarlılığı olarak beyan etmenin doğru yoludur. Manifestinizle uğraşmadan çabucak denemek istiyorsanız, arayın SetProcessDPIAware. Bunu, uygulamanız çalışırken yaptığınız ilk şey olarak yapın. Tercihen erken birim başlatma bölümlerinden birinde veya .dpr dosyanızdaki ilk şey olarak.

Uygulamanızı yüksek DPI duyarlılığı olarak beyan etmezseniz, Vista ve üstü,% 125'in üzerindeki herhangi bir yazı tipi ölçeklendirmesi için onu eski modda işleyecektir. Bu oldukça korkunç görünüyor. O tuzağa düşmekten kaçınmaya çalışın.

Monitör başına Windows 8.1 DPI güncellemesi

Windows 8.1'den itibaren, artık monitör başına DPI ayarları için işletim sistemi desteği bulunmaktadır ( http://msdn.microsoft.com/en-ca/magazine/dn574798.aspx ). Bu, çok farklı yeteneklere sahip farklı ekranlara sahip olabilen modern cihazlar için büyük bir sorundur. Çok yüksek DPI'lı bir dizüstü bilgisayar ekranınız ve düşük DPI'lı bir harici projektörünüz olabilir. Böyle bir senaryoyu desteklemek, yukarıda anlatılandan daha fazla çalışma gerektirir.


2
Bu her zaman doğru değildir. Aslında, Ölçeklendirildi = true olarak ayarlamak ve ardından Yüksek DPI duyarlılığını ayarlamak, çoğu delphi uygulamasında bazı tuhaf kırılmalara neden olabilir. Uygulamalarımın yüksek DPI'da çalışmasını sağlamak için yüzlerce saat harcadım ve korkunç görünen pikselleşmeye sahip olmanın kontrollerin kırpılmasından, ekrandan kaydırılmasından, çeşitli kontrollerde fazladan veya eksik kaydırma çubuklarından daha iyi olduğunu buldum.
Warren P

@WarrenP Bence bu sorunlar uygulamanıza özel. Kişisel deneyimim, Delphi uygulamamın% 200 yazı tipi ölçeklendirmesinde bile mükemmel görüntülenmesi ve ölçeklenmesi.
David Heffernan

2
@WarrenP Ne olmuş yani? Delphi IDE'den daha iyi davranan uygulamalar oluşturmak için Delphi'yi kullanmak tamamen mümkündür.
David Heffernan

1
Delphi 5,6,7 ile oluşturulan sabit kenarlara ve başarısız olacak şekilde ölçeklendirilmiş ayarlara sahip birçok diyalog gördüm. Tamam gizleme, iptal düğmeleri vb. Delphi2006'daki bazı diyaloglar bile bunun tarafından ısırıldığını düşünüyor. Yerel Delphi bileşenlerini ve Windows bileşenlerini karıştırmak da garip efektler verir. GUI'yi her zaman% 125 yazı tipi ölçeklendirmesinde geliştiririm ve scaled özelliğini false olarak koyarım.
LU RD

2
Harika şeyler. Harika bilgiler için +1. Benim fikrim (yapma) bunu yapmak istediğinizde bunu NASIL yapacağınızı bilme ihtiyacının ikinci önemi ...
Warren

42

Kullanıcının DPI'sını onurlandırmanın, gerçek işinizin yalnızca bir alt kümesi olduğunu da unutmamak gerekir:

kullanıcının yazı tipi boyutunu onurlandırmak

On yıllardır Windows bu sorunu piksel yerine Dialog Units kullanarak gerçekleştiren mizanpajla çözdü . Bir "iletişim birimi" bu fontun böylece tanımlanır ortalama karakter olduğunu

  • 4 diyalog birimi (dlus) genişliğinde ve
  • 8 diyalog birimi (clus) yüksek

görüntü açıklamasını buraya girin

Delphi, Scaledbir formun otomatik olarak

  • Kullanıcının Windows DPI ayarları, ayetler
  • formu en son kaydeden geliştiricinin makinesindeki DPI ayarı

Bu, kullanıcı formu tasarladığınızdan farklı bir yazı tipi kullandığında sorunu çözmez, örneğin:

  • geliştirici formu MS Sans Serif 8pt ile tasarladı (ortalama karakter 6.21px x 13.00px96 dpi'de )
  • Tahoma 8pt ile çalışan kullanıcı (ortalama karakter 5.94px x 13.00px96 dpi'de )

    Windows 2000 veya Windows XP için bir uygulama geliştiren herkes için olduğu gibi.

veya

  • geliştirici formu ** Tahoma 8pt * ile tasarladı (ortalama karakter 5.94px x 13.00px96 dpi'de)
  • Segoe UI 9pt ile çalışan bir kullanıcı (burada ortalama karakter 6.67px x 15px96 dpi'de )

İyi bir geliştirici olarak, kullanıcınızın yazı tipi tercihlerini onurlandıracaksınız. Bu, formunuzdaki tüm denetimleri yeni yazı tipi boyutuyla eşleşecek şekilde ölçeklemeniz gerektiği anlamına gelir:

  • her şeyi yatay olarak% 12.29 (6.67 / 5.94) genişlet
  • her şeyi dikey olarak% 15,38 (15/13) uzat

Scaled bunu senin için halletmeyecek.

Şu durumlarda daha da kötüleşir:

  • Formunuzu Segoe UI 9pt'de tasarladı (Windows Vista, Windows 7, Windows 8 varsayılanı)
  • kullanıcı Segoe UI 14pt çalıştırıyor (örneğin benim tercihim)10.52px x 25px

Şimdi her şeyi ölçeklendirmelisin

  • yatay olarak% 57,72
  • dikey olarak% 66.66

Scaled bunu senin için halletmeyecek.


Akıllıysanız, DPI'yı onurlandırmanın ne kadar önemsiz olduğunu görebilirsiniz:

  • Segoe UI 9pt @ 96dpi (6.67px x 15px) ile tasarlanmış form
  • Segoe UI 9pt @ 150dpi (10.52px x 25px) ile çalışan kullanıcı

Kullanıcının DPI ayarına bakmamalısınız, yazı tipi boyutlarına bakmalısınız . İki kullanıcı koşuyor

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

aynı yazı tipini çalıştırıyor . DPI, yazı tipi boyutunu etkileyen şeylerden yalnızca biridir ; kullanıcının tercihleri ​​diğeridir.

StandardizeFormFont

Clovis StandardizeFormFont, bir formdaki yazı tipini düzelten ve onu yeni yazı tipi boyutuna ölçekleyen bir işleve başvurduğumu fark etti . Bu standart bir işlev değil, Borland'ın hiçbir zaman üstesinden gelemediği basit görevi yerine getiren bir dizi işlevdir.

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 6 farklı yazı tipine sahiptir; Windows'ta tek bir "yazı tipi ayarı" yoktur.
Ancak deneyimlerimizden formlarımızın Simge Başlığı Yazı Tipi ayarına uyması gerektiğini biliyoruz

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;

Biz formu ölçeklendirecektir yazı tipi boyutunu öğrendikten sonra hiç biz (formun geçerli yazı tipi yüksekliği elde piksel ) ve bu faktörü ile büyütmek.

Örneğin, formu olarak ayarlıyorsam ve form -16şu anda konumundaysa -11, tüm formu şuna göre ölçeklendirmemiz gerekir:

-16 / -11 = 1.45454%

Standardizasyon iki aşamada gerçekleşir. Önce formu yeni: eski yazı tipi boyutlarının oranına göre ölçeklendirin. Sonra yeni yazı tipini kullanmak için kontrolleri (özyinelemeli olarak) değiştirin.

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;

İşte bir formu gerçekten ölçeklendirme işi. Borland'ın kendi Form.ScaleByyönteminde böcekler etrafında çalışır . Öncelikle form üzerindeki tüm bağlantıları devre dışı bırakması, ardından ölçeklendirmeyi gerçekleştirmesi ve ardından bağlantıları yeniden etkinleştirmesi gerekir:

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;

ve sonra yeni yazı tipini tekrar tekrar kullanmalıyız :

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;

Çapalar yinelemeli olarak devre dışı bırakıldığında:

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;

Ve çapalar yinelemeli olarak yeniden etkinleştiriliyor:

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;

Şu şekilde bırakılan bir kontrol yazı tipini gerçekten değiştirme çalışmasıyla:

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;

Bu sandığınızdan çok daha fazla kod; biliyorum. Üzücü olan şey, uygulamalarını gerçekten doğru yapan benim dışımda dünyada Delphi geliştiricisi olmaması.

Sayın Delphi Geliştiricisi : Windows yazı tipinizi Segoe UI 14pt olarak ayarlayın ve buggy uygulamanızı düzeltin

Not : Herhangi bir kod kamu malı olarak yayınlanır. Herhangi bir atıf gerekmez.


1
Cevabınız için teşekkürler, ama gerçek dünya için ne önerirsiniz? Tüm kontrolleri manuel olarak yeniden boyutlandırmak mı?
LaBracca

3
"Üzücü olan şey, uygulamalarını gerçekten doğru yapan benim dışımda, dünyada Delphi geliştiricisi olmaması." Bu çok kibirli ve yanlış bir ifade. Cevabımdan : Aslında, ScaleFromSmallFontsDimension sürümüm, çalışma zamanında form fontunun tasarım zamanında ayarlanandan farklı olma olasılığını da hesaba katıyor. Ölçeklendirme bunu hesaba katmalıdır çünkü kaynak kodda kullanılan mutlak boyut değerlerinin, 96dpi'de 8pt Tahoma'nın taban çizgisine göre olduğu varsayılır. Seninki sana iyi bir cevap, +1.
David Heffernan

1
@Ian bunu söyleyen ben değilim. Warren'a benziyor.
David Heffernan

2
Bu oldukça harika Ian. Teşekkürler.
Warren P

2
Son zamanlarda bu soru ve cevapla karşılaştım. Ian'ın tüm kodunu burada çalışan bir birimde topladım: pastebin.com/dKpfnXLc ve Google + 'da burada yayınladım: goo.gl/0ARdq9 Birinin bunu yararlı bulması durumunda burada yayınlama .
W.Prins

11

İşte hediyem. GUI düzenlerinizdeki öğelerin yatay konumlandırılmasına yardımcı olabilecek bir işlev. Herkes tek.

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;

2
Beğenmene sevindim Warren. Çözmem gereken sorun için hiçbir çözüm bulunamadığında yaklaşık 15 yaşında. Ve bugün bile uygulanabileceği bir durum olabilir. B-)
avra
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.