Windows 글꼴 배율이 100 %보다 클 때 GUI가 제대로 작동하도록하려면 어떻게해야합니까?


107

Windows 제어판에서 큰 글꼴 크기 (예 : 125 % 또는 150 %)를 선택하면 무언가가 픽셀 단위로 설정 될 때마다 VCL 응용 프로그램에 문제가 있습니다.

를 타고 TStatusBar.Panel. 정확히 하나의 레이블을 포함하도록 너비를 설정했습니다. 이제 큰 글꼴에는 "오버플로"레이블이 있습니다. 다른 구성 요소와 동일한 문제입니다.

Dell의 일부 새 노트북은 기본 설정으로 125 %가 이미 제공되므로 과거에는이 문제가 매우 드물었지만 이제는 매우 중요합니다.

이 문제를 극복하기 위해 무엇을 할 수 있습니까?

답변:


56

참고 : 다른 답변에는 매우 유용한 기술이 포함되어 있으므로 참조하십시오. 여기 내 대답은 DPI 인식이 쉽다고 가정하는 것에 대한 경고와주의 사항만을 제공합니다.

나는 일반적으로 TForm.Scaled = True. DPI 인식은 저에게 전화를 걸어 비용을 지불 할 용의가있는 고객에게 중요해질 때만 중요합니다. 이 관점의 기술적 이유는 DPI 인식 여부에 관계없이 상처의 세계로 창을 열고 있기 때문입니다. 많은 표준 및 타사 VCL 컨트롤이 High DPI에서 제대로 작동하지 않습니다. Windows Common Control을 래핑하는 VCL 부분이 높은 DPI에서 현저하게 잘 작동한다는 주목할만한 예외입니다. 수많은 타사 및 내장 Delphi VCL 사용자 지정 컨트롤이 높은 DPI에서 제대로 작동하지 않거나 전혀 작동하지 않습니다. TForm.Scaled를 켜려는 경우 프로젝트의 모든 단일 양식과 사용하는 모든 단일 타사 및 기본 제공 컨트롤에 대해 96, 125 및 150 DPI에서 테스트해야합니다.

Delphi 자체는 Delphi로 작성되었습니다. 대부분의 양식에 대해 High DPI 인식 플래그가 켜져 있지만 Delphi XE2에서와 같이 최근에도 IDE 작성자는 High DPI Awareness 매니페스트 플래그를 설정하지 않기로 결정했습니다. Delphi XE4 이상에서는 HIGH DPI 인식 플래그가 켜져 있고 IDE가 좋아 보입니다.

TForm.Scaled = true (Delphi의 기본값이므로 수정하지 않는 한 대부분의 양식에는 Scaled = true가 있음)를 High DPI Aware 플래그 (David의 답변에 표시됨)와 함께 사용하지 않는 것이 좋습니다. 내장 델파이 양식 디자이너를 사용하여 빌드 된 VCL 애플리케이션.

나는 과거에 TForm.Scaled가 참이고 Delphi 폼 스케일링에 결함이있을 때 예상 할 수있는 종류의 파손에 대한 최소한의 샘플을 만들려고 시도했습니다. 이러한 결함이 항상 발생하는 것은 아니며 96 이외의 DPI 값에 의해서만 발생합니다. Windows XP 글꼴 크기 변경을 포함하여 다른 항목의 전체 목록을 확인할 수 없었습니다. 그러나 이러한 결함의 대부분은 내 응용 프로그램에서만 나타나기 때문에 상당히 복잡한 상황에서 스스로 확인할 수있는 몇 가지 증거를 보여 주기로 결정했습니다.

Windows 7에서 DPI Scaling을 "Fonts @ 200 %"로 설정하면 Delphi XE는 다음과 같이 보입니다. Delphi XE2는 Windows 7 및 8에서 유사하게 손상되지만 이러한 결함은 Delphi XE4에서 수정 된 것으로 보입니다.

여기에 이미지 설명 입력

여기에 이미지 설명 입력

이들은 대부분 높은 DPI에서 오작동하는 표준 VCL 컨트롤입니다. 대부분의 항목은 전혀 확장되지 않았으므로 Delphi IDE 개발자는 DPI 인식을 무시하고 DPI 가상화를 해제하기로 결정했습니다. 흥미로운 선택입니다.

이 새로운 추가적인 고통의 원인과 어려운 선택을 원하는 경우에만 DPI 가상화를 끄십시오. 내버려 두는 것이 좋습니다. Windows 일반 컨트롤은 대부분 잘 작동하는 것 같습니다. Delphi 데이터 탐색기 컨트롤은 표준 Windows 트리 공통 컨트롤을 둘러싼 C # WinForms 래퍼입니다. 이는 순수한 Microsoft 결함이며이를 수정하려면 Embarcadero가 데이터 탐색기에 대한 순수한 네이티브 .Net 트리 컨트롤을 다시 작성하거나 일부 DPI 확인 및 수정 속성 코드를 작성하여 컨트롤의 항목 높이를 변경해야 할 수 있습니다. Microsoft WinForms조차도 높은 DPI를 사용자 지정 kludge 코드없이 자동으로 깔끔하게 처리 할 수 ​​없습니다.

업데이트 : 흥미로운 사실 ​​: 델파이 IDE는 "가상화"되지 않은 것처럼 보이지만, "비 DPI 가상화"를 달성하기 위해 David가 표시 한 매니페스트 콘텐츠를 사용하지 않습니다. 아마도 런타임에 일부 API 기능을 사용하고있을 것입니다.

업데이트 2 : 100 % / 125 % DPI를 지원하는 방법에 대한 응답으로 2 단계 계획을 마련했습니다. 1 단계는 높은 DPI를 위해 수정해야하는 사용자 지정 컨트롤에 대한 내 코드를 인벤토리 화 한 다음이를 수정하거나 단계적으로 제거 할 계획을 세우는 것입니다. 2 단계는 레이아웃 관리없이 양식으로 디자인 된 코드의 일부 영역을 일부 레이아웃 관리를 사용하는 양식으로 변경하여 DPI 또는 글꼴 높이 변경이 클리핑없이 작동 할 수 있도록하는 것입니다. 나는이 "컨트롤 간"레이아웃 작업이 "컨트롤 내"작업보다 대부분의 애플리케이션에서 훨씬 더 복잡 할 것이라고 생각합니다.

업데이트 : 2016 년에 최신 Delphi 10.1 Berlin이 150dpi 워크 스테이션에서 잘 작동합니다.


5
해당 API 함수는 SetProcessDPIAware.
David Heffernan 2011

2
우수한. 새로운 사실에 감사드립니다. 가능한 한 가지 경로로 제안하도록 답변을 수정하는 것이 좋습니다. 고객이 해당 옵션을 구성하기를 원할 수도 있습니다 (작동하지 않는 경우 해제).
Warren P

Delphi의 스플래시 화면은 DPI 가상화를 사용합니다. 아마도 SetDPIAware에 대한 호출이 스플래시 양식이 이미 표시된 후이기 때문일 것입니다.
Warren P

6
RAD Studio는 표준 VCL 컨트롤, 사용자 지정 컨트롤, .NET WinForms 및 FireMonkey 양식의 큰 조합입니다. 문제가 있다는 것은 놀라운 일이 아닙니다. 이것이 RAD Studio가 좋은 예가 아닌 이유입니다.
Torbins 2011

1
당신이 옳다면, 모래에 머리를 가진 것은 VCL 자체입니다. 마이크로 소프트조차도 모래 속에 머리가 있습니다. 내가 지금까지 사용한 유일한 프레임 워크는 Mac의 COCOA입니다.
Warren P

63

.dfm 파일의 설정 ScaledTrue.

코드에서 차원을 설정하는 경우으로 Screen.PixelsPerInch나누어 크기를 조정해야합니다 Form.PixelsPerInch. MulDiv이를 위해 사용하십시오 .

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

이것은 양식 지속성 프레임 워크 ScaledTrue.

사실,이 함수를 분모로 96의 값을 하드 코딩하는 버전으로 대체하기 위해 cogent 인수를 만들 수 있습니다. 이를 통해 절대 치수 값을 사용할 수 있으며 개발 시스템에서 글꼴 크기 조정을 변경하고 .dfm 파일을 다시 저장하는 경우 의미 변경에 대해 걱정할 필요가 없습니다. 중요한 이유 PixelsPerInch는 .dfm 파일에 저장된 속성이 .dfm 파일이 마지막으로 저장된 시스템의 값이기 때문입니다.

const
  SmallFontsPixelsPerInch = 96;

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

따라서 테마를 계속 진행하면서주의해야 할 또 다른 점은 프로젝트가 DPI 값이 다른 여러 시스템에서 개발 된 경우 Delphi가 .dfm 파일을 저장할 때 사용하는 스케일링으로 인해 일련의 편집 작업을 헤매는 컨트롤이 발생한다는 것입니다. . 제 직장에서는이를 방지하기 위해 양식이 96dpi (100 % 스케일링)로만 편집된다는 엄격한 정책이 있습니다.

사실 내 버전은 ScaleFromSmallFontsDimension런타임에 디자인 타임에 설정된 글꼴과 다른 양식 글꼴의 가능성을 허용합니다. XP 컴퓨터에서 내 응용 프로그램의 양식은 8pt Tahoma를 사용합니다. Vista 이상에서는 9pt Segoe UI가 사용됩니다. 이것은 또 다른 자유도를 제공합니다. 소스 코드에 사용 된 절대 치수 값은 96dpi에서 8pt Tahoma의 기준선에 상대적인 것으로 가정하므로 스케일링은이를 고려해야합니다.

UI에서 이미지 또는 글리프를 사용하는 경우 이것도 확장해야합니다. 일반적인 예는 도구 모음과 메뉴에 사용되는 글리프입니다. 이러한 글리프를 실행 파일에 링크 된 아이콘 리소스로 제공 할 수 있습니다. 각 아이콘은 다양한 크기를 포함해야하며 런타임시 가장 적절한 크기를 선택하고 이미지 목록에로드합니다. 해당 주제에 대한 세부 정보는 여기에서 찾을 수 있습니다. 별칭을 사용하지 않고 리소스에서 아이콘을로드하려면 어떻게합니까?

또 다른 유용한 트릭은 TextWidth또는 에 상대적인 상대적 단위로 치수를 정의하는 것 TextHeight입니다. 따라서 크기가 약 10 개의 수직선이되도록하려면을 사용할 수 있습니다 10*Canvas.TextHeight('Ag'). 이것은 줄 간격 등을 허용하지 않기 때문에 매우 거칠고 준비된 메트릭입니다. 그러나, 종종 당신이해야 할 일은 GUI가 PixelsPerInch.

또한 응용 프로그램을 높은 DPI 인식 으로 표시해야합니다 . 이를 수행하는 가장 좋은 방법은 애플리케이션 매니페스트를 사용하는 것입니다. Delphi의 빌드 도구는 매니페스트를 사용자 정의하는 것을 허용하지 않기 때문에이를 사용하면 자신의 매니페스트 리소스를 연결해야합니다.

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

리소스 스크립트는 다음과 같습니다.

1 24 "Manifest.txt"

여기서 Manifest.txt실제 매니페스트가 포함되어 있습니다. 또한 comctl32 v6 섹션을 포함하고로 설정 requestedExecutionLevel해야 asInvoker합니다. 그런 다음이 컴파일 된 리소스를 앱에 연결하고 Delphi가 매니페스트를 사용하여 동일한 작업을 시도하지 않도록합니다. 최신 Delphi에서는 Runtime Themes 프로젝트 옵션을 None으로 설정하여이를 달성합니다.

매니페스트는 앱이 높은 DPI를 인식하도록 선언 하는 올바른 방법입니다. 매니페스트를 엉망으로 만들지 않고 빠르게 시도하려면 SetProcessDPIAware. 앱이 실행될 때 가장 먼저 수행하는 작업을 수행하십시오. 초기 단위 초기화 섹션 중 하나에서 또는 .dpr 파일의 첫 번째 항목으로 사용하는 것이 좋습니다.

높은 DPI를 인식하도록 앱을 선언하지 않으면 Vista 이상은 125 % 이상의 글꼴 크기 조정에 대해 레거시 모드로 렌더링합니다. 이것은 매우 무서운 것 같습니다. 그 함정에 빠지지 않도록하십시오.

모니터 DPI 업데이트 당 Windows 8.1

Windows 8.1부터 모니터 별 DPI 설정에 대한 OS 지원이 있습니다 ( http://msdn.microsoft.com/en-ca/magazine/dn574798.aspx ). 이것은 매우 다른 기능을 가진 다른 디스플레이가 부착 된 최신 장치의 경우 큰 문제입니다. DPI 노트북 화면이 매우 높고 DPI가 낮은 외부 프로젝터가있을 수 있습니다. 이러한 시나리오를 지원하려면 위에서 설명한 것보다 더 많은 작업이 필요합니다.


2
그것은 항상 사실이 아닙니다. 실제로 Scaled = true로 설정 한 다음 높은 DPI 인식을 설정하면 대부분의 델파이 응용 프로그램에서 이상한 손상이 발생할 수 있습니다. 저는 앱이 높은 DPI에서 작동하도록 수백 시간을 보냈으며 컨트롤이 잘 리거나 화면 밖으로 이동하거나 다양한 컨트롤에서 스크롤바가 추가되거나 누락되는 것보다 끔찍하게 보이는 픽셀 화를 갖는 것이 더 낫다는 것을 알게되었습니다.
Warren P

@WarrenP 이러한 문제는 앱에만 국한된 것이라고 생각합니다. 내 개인적인 경험은 내 Delphi 앱이 200 % 글꼴 크기 조정에서도 완벽하게 표시되고 크기 조정된다는 것입니다.
David Heffernan

2
@WarrenP 그래서 뭐? Delphi를 사용하여 Delphi IDE보다 더 잘 작동하는 앱을 빌드하는 것은 완벽하게 가능합니다.
David Heffernan 2011

1
Delphi 5,6,7로 만든 고정 테두리가있는 많은 대화 상자를 보았습니다. 확인, 취소 버튼 등 숨기기. Delphi2006의 일부 대화 상자조차도 이것에 물렸다 고 생각합니다. 기본 Delphi 구성 요소와 Windows 구성 요소를 혼합하면 이상한 효과가 나타납니다. 저는 항상 125 % 글꼴 크기로 GUI를 개발하고 scaled 속성을 false로 설정합니다.
LU RD

2
훌륭한 물건. 환상적인 정보를 얻으려면 +1하십시오. 내 의견 (하지 마세요)은이 작업을 수행하고 싶을 때 어떻게해야하는지 알 필요가 두 번째로 중요합니다.
Warren P

42

사용자의 DPI를 존중하는 것은 실제 작업의 일부일 뿐이라는 점에 유의해야합니다.

사용자의 글꼴 크기 존중

수십 년 동안 Windows는 픽셀이 아닌 Dialog Units를 사용하여 레이아웃을 수행한다는 개념으로이 문제를 해결했습니다 . "대화 단위는" 해당 글꼴의 있도록 정의 된 평균 문자 입니다

  • 4 개의 대화 단위 (dlus) 너비 및
  • 8 개의 대화 단위 (clus) 높이

여기에 이미지 설명 입력

델파이는 (버기) 개념과 함께 제공 Scaled됩니다.

  • 사용자의 Windows DPI 설정, 구절
  • 양식을 마지막으로 저장 한 개발자 컴퓨터의 DPI 설정

사용자가 양식을 디자인 한 것과 다른 글꼴을 사용하면 문제가 해결되지 않습니다. 예 :

  • 개발자는 MS Sans Serif 8pt (평균 문자는 6.21px x 13.00px96dpi)로 양식을 설계했습니다.
  • Tahoma 8pt로 실행하는 사용자 (여기서 평균 문자는 5.94px x 13.00px96dpi)

    Windows 2000 또는 Windows XP 용 응용 프로그램을 개발하는 사람의 경우도 마찬가지입니다.

또는

  • 개발자는 ** Tahoma 8pt * (평균 문자는 5.94px x 13.00px96dpi)로 양식을 설계했습니다.
  • Segoe UI 9pt (평균 문자는 6.67px x 15px96dpi)로 실행하는 사용자

좋은 개발자는 사용자의 글꼴 기본 설정을 존중할 것입니다. 즉, 새 글꼴 크기와 일치하도록 양식의 모든 컨트롤을 조정해야합니다.

  • 모든 것을 가로로 12.29 % 확장 (6.67 / 5.94)
  • 모든 것을 수직으로 15.38 % 늘이기 (15/13)

Scaled 당신을 위해 이것을 처리하지 않을 것입니다.

다음과 같은 경우 악화됩니다.

  • Segoe UI 9pt (Windows Vista, Windows 7, Windows 8 기본값) 에서 양식 디자인
  • 사용자가 Segoe UI 14pt를 실행 하고 있습니다.10.52px x 25px

이제 모든 것을 확장해야합니다.

  • 가로로 57.72 %
  • 수직 66.66 %

Scaled 당신을 위해 이것을 처리하지 않을 것입니다.


똑똑하다면 DPI를 존중하는 것이 얼마나 무관심한지 알 수 있습니다.

  • Segoe UI 9pt @ 96dpi (6.67px x 15px)로 디자인 된 양식
  • Segoe UI 9pt @ 150dpi (10.52px x 25px)로 실행중인 사용자

사용자의 DPI 설정을 확인하지 말고 글꼴 크기 를 확인해야합니다 . 두 명의 사용자가 실행 중

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

동일한 글꼴을 실행하고 있습니다. DPI는 글꼴 크기에 영향을 미치는 요소 중 하나 일뿐입니다 . 사용자의 선호도는 다른 것입니다.

StandardizeFormFont

Clovis는 내가 StandardizeFormFont양식의 글꼴을 수정하고 새 글꼴 크기로 크기를 조정 하는 함수 를 참조한다는 사실을 발견했습니다 . 표준 기능이 아니라 볼랜드가 처리하지 않은 간단한 작업을 수행하는 전체 기능 집합입니다.

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 가지 글꼴이 있습니다. Windows에는 단일 "글꼴 설정"이 없습니다.
그러나 우리는 양식이 아이콘 제목 글꼴 설정을 따라야한다는 것을 경험으로 알고 있습니다.

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;

우리는 우리가 형태로 확장 할 수 글꼴 크기를 알고 나면 로를 , 우리는 (폼의 현재 글꼴 높이 얻을 픽셀을 ), 그 요인에 의해 확장 할 수 있습니다.

예를 들어 양식을로 설정하고 -16현재 양식이에있는 -11경우 다음과 같이 전체 양식의 크기를 조정해야합니다.

-16 / -11 = 1.45454%

표준화는 두 단계로 이루어집니다. 먼저 새 글꼴 크기의 비율로 양식의 크기를 조정합니다. 그런 다음 실제로 새 글꼴을 사용하도록 컨트롤을 재귀 적으로 변경합니다.

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;

실제로 폼 크기를 조정하는 작업이 있습니다. 볼랜드 자체 Form.ScaleBy방법 의 버그를 해결합니다 . 먼저 양식의 모든 앵커를 비활성화 한 다음 크기 조정을 수행 한 다음 앵커를 다시 활성화해야합니다.

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;

그런 다음 실제로 새 글꼴 을 재귀 적으로 사용해야 합니다.

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;

앵커가 재귀 적으로 비활성화 된 경우 :

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;

앵커는 재귀 적으로 다시 활성화됩니다.

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;

실제로 컨트롤 글꼴을 변경하는 작업은 다음과 같습니다.

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;

그것은 당신이 생각했던 것보다 훨씬 더 많은 코드입니다. 알아. 슬픈 점은 실제로 응용 프로그램을 올바르게 만드는 나를 제외하고는 Delphi 개발자가 지구상에 없다는 것입니다.

Dear Delphi 개발자 : Windows 글꼴을 Segoe UI 14pt 로 설정하고 버그가있는 응용 프로그램을 수정하십시오.

참고 : 모든 코드는 공개 도메인으로 공개됩니다. 귀속이 필요하지 않습니다.


1
답변 해 주셔서 감사합니다.하지만 현실 세계에 무엇을 제안 하시겠습니까? 모든 컨트롤의 크기를 수동으로 구현 하시겠습니까?
LaBracca

3
"슬프게도 실제로 응용 프로그램을 수정하는 델파이 개발자는 저를 제외하고는 지구상에 델파이 개발자가 없다는 것입니다." 그것은 잘못된 매우 오만한 진술입니다. 내 대답에서 : 실제로 ScaleFromSmallFontsDimension의 내 버전은 런타임에 디자인 타임에 설정된 글꼴과 다른 양식 글꼴의 가능성을 허용합니다. 소스 코드에 사용 된 절대 치수 값은 96dpi에서 8pt Tahoma의 기준선에 상대적인 것으로 가정하므로 스케일링은이를 고려해야합니다. 당신은 좋은 대답입니다. +1.
David Heffernan

1
@Ian 그렇게 말한 내가 아닙니다. 워렌처럼 들립니다.
David Heffernan

2
이건 정말 대단해요, 이안 감사.
Warren P

2
최근에이 질문과 답변을 만났습니다. Ian의 모든 코드를 여기에서 작업 단위로 수집했습니다 : pastebin.com/dKpfnXLc 그리고 여기에 Google+에 게시했습니다 : goo.gl/0ARdq9 누군가가 유용하다고 생각하는 경우 여기에 게시.
W.Prins 2015 년

11

여기 내 선물이 있습니다. GUI 레이아웃에서 요소의 수평 위치 지정을 도와주는 기능입니다. 모두를위한 무료.

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
워렌이 마음에 들어서 기뻐요. 제가 해결해야 할 문제에 대한 해결책이 없었던 것은 약 15 년 전입니다. 그리고 오늘날에도 적용될 수있는 상황이있을 수 있습니다. B-)
avra
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.