Windows Forms High DPI 대응

Windows Forms의 High DPI에 관한 고민을 오랫동안 해오다가 최근에 파악한 내용들을 바탕으로 방법을 정리해보았습니다.

이미지 출처: https://www.youtube.com/watch?v=pO4v_c6-hk0

작업할 때 고려할 사항

만들어진 FormAutoScaleModeDpi로 변경합니다. 그리고 이렇게 변경한 다음부터는 기존에 만들어진 컨트롤들의 크기를 계산하여 적절한 값을 대입해야 합니다. 디자인 타임에서 사용하는 모든 단위는 96 DPI를 기준으로 맞추어져 있어서, 배율에 따라 값을 곱해서 늘려서 사용해야 합니다.

예를 들어 폭 800에 높이 600으로 Form을 만들어 사용해왔다면, 현재 작업하고 있는 배율이 200%라고 했을 때 폭은 200%를 소수로 변환했을 때의 값인 2.0을 곱하여 1600으로, 높이도 2.0을 곱하여 1200으로 Size 속성에 값을 대입해야 나중에 낮은 배율의 화면에서 실행했을 때에도 레이아웃이 깨지지 않습니다.

주의할 것은, 해상도가 다른 모니터 여러대를 확장 모드로 놓고 사용할 경우, 어느 화면에서 Visual Studio를 띄워서 작업하는가에 따라 위의 속성 값 계산을 일일이 따져봐야 하는 문제가 발생합니다. 레이아웃이 의도하지 않게 깨지는 것을 예방하려면, 모든 화면의 배율을 맞추거나, 한 화면만 사용하도록 디스플레이 설정을 바꾸는 것이 좋습니다.

문제를 단순화하기

만약 이와 같이 크기 계산을 하여 작업하는 것이 번거롭다면, Visual Studio 자체의 호환성 설정을 이용하여 Windows Forms 화면을 다루는 동안 DPI 배율을 강제로 100%로 낮추어 작업하는 것이 더 간편할 수 있습니다. 이렇게 하면 배율로 1.0을 곱하는 것으로, 계산이 필요하지 않습니다.

출처: https://docs.microsoft.com/ko-kr/dotnet/framework/winforms/automatic-scaling-in-windows-forms

단, 이 작업을 실행하려면 시스템 관리자 권한이 필요하며, 레지스트리 설정을 수정하는 것이므로, 레지스트리 설정 편집 등을 잘 모르는 경우 시스템 백업 등을 사전에 수행하는 것을 권장합니다.

  • 실행 중인 Visual Studio를 모두 종료합니다.
  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options 를 레지스트리 편집기로 엽니다.
  • devenv.exe 키를 찾거나, 없으면 새로 만듭니다.
  • 32비트 DWORD 타입의 항목을 새로 만들고, 이름은 dpiAwareness 로 하나 만듭니다. 배율을 100%로 고정시켜 실행하려면 값을 0 으로, 고정하지 않으려면 해당 항목을 지우거나 값을 1 로 저장합니다.
  • Visual Studio를 다시 시작합니다.

배율 조정을 가한 상태에서 Visual Studio를 실행하면 배율 변경 때마다 사용자를 로그아웃하거나 시스템을 다시 시작하지 않아도 빠르게 작업을 전환할 수 있습니다.

디자인 타임과의 상호 작용

디자인 타임에서는 AutoScaleModeDpi로 설정하였을 경우, 자동으로 AutoScaleDimension 이라는 감춰진 속성에 값을 대입합니다. 이 때, 현재 Visual Studio가 실행 중인 화면의 배율에 따라 값을 고치게 됩니다. 여기에 맞추어 디자인 타임 내 모든 컨트롤들의 Size 속성의 값도 같이 고쳐지게 됩니다.

어떤 배율 환경에서 작업하던지 간에, 크기 값을 정확하게 지정하였다면 이와 같은 메커니즘으로 디자인 타임과 런타임의 컨트롤 크기가 자동 결정되는 것이라고 이해할 수 있습니다.

단, 이러한 메커니즘에서 주의해야 할 부분은 소수점 연산으로 인하여 컨트롤의 위치 좌표에 미세한 변화가 일어날 수 있다는 점입니다. Size 속성에 대입하는 값은 소수점 이하 자리를 지원하지 않는 정수값만 지원되기 때문에, 컨트롤들의 배열을 너무 세밀하게 배치한다면 이러한 변화 때문에 문제가 발생할 수 있습니다.

따라서 제일 바람직한 레이아웃 설계 방법은, Dock, Anchor 프로퍼티를 이용하는 방식이나, 각종 컨테이너 컨트롤 (TableLayoutPanel, StackLayoutPanel 등)을 활용하는 방식입니다.

응용프로그램 설정 업데이트하기

위와 같이 디자인 타임에서의 문제를 파악하고 수정한 다음에는, 런타임 설정을 변경하여 최적의 상태로 실행될 수 있게 만들어주는 것이 필요합니다.

우선 app.manifest 파일을 추가합니다. 아래 그림과 같이 새 프로젝트 항목으로 Application Manifest 를 찾으면 됩니다.

응용프로그램 매니페스트 추가

assembly 태그 아래에 다음의 XML 마크업을 추가합니다. dpiAware 태그의 값을 true로 변경합니다.

<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>

마지막으로 app.config 파일을 추가하고 <appSettings> 태그에 아래의 코드를 추가합니다. valuetrue가 아닌 True로 설정되어야 함을 주의합니다.참고로 이 설정을 사용하려면 .NET Framework 4.6 이상을 타겟으로 빌드하고 실행해야 합니다.

<add key=”EnableWindowsFormsHighDpiAutoResizing” value=”True” />

만약 위와 같이 작업할 수 없다면

만약 소스 코드를 사용할 수 없거나,유지보수가 곤란한 레이아웃을 가진 응용프로그램이라면, 반대로 DPI Awareness를 끄도록 호환성 옵션을 맞추거나, 위의 매니페스트 설정에서 DPI Awareness를 끄도록 (모든 값을 False로 변경) 하면 Windows가 강제로 배율에 맞추어 화면을 늘여서 그리도록 할 수 있습니다.

이렇게 하면, 텍스트나 그림 등이 뿌옇게 보이는 부작용이 발생하지만, Windows 10 2018년 4월 업데이트 버전부터는 이 문제를 완화하는 기능을 추가로 제공하여 실 사용에 최대한 불편함이 없게 만들 수는 있습니다. 그러나 완전한 기능이 아니며, 렌더링에 필요한 리소스 소비가 좀 더 많을 수 있습니다. (참조: https://support.microsoft.com/ko-kr/help/4091364/windows-10-fix-blurry-apps)