Giao diện Windows Forms trên màn hình DPI cao

Với sự phát triển của công nghệ, màn hình có DPI cao ngày càng trở nên phổ biến. Tuy nhiên, vì ra đời trong thời đại mà thiết bị DPI cao rất hiếm, Windows Forms không được thiết kế để hiển thị tốt trên các loại màn hình này.

Chỉ số DPI thông dụng là 96, nghĩa là có 96 pixel trong diện tích 1 inch vuông. Các thiết bị mới như máy tính bảng Surface hoặc laptop cao cấp của Dell, HP đều hỗ trợ DPI trên 200. Khi ứng dụng được chạy trong môi trường DPI cao, Windows phải thực hiện thao tác “scaling” để canh chỉnh lại các thành phần trong giao diện. Nếu giữ nguyên độ scale ban đầu (100%), mọi thứ sẽ trở nên nhỏ xíu.

Vì được “scale” tự động, giao diện ứng dụng sẽ bị mờ, font chữ không còn rõ nét, đôi lúc kích thước, vị trí của các thành phần trong form cũng không còn chính xác như lúc thiết kế. Những lỗi này tuy không ảnh hưởng đến chức năng chương trình nhưng vẫn khiến người dùng khó chịu.

Hình dưới đây là một ứng dụng Windows Form khi hiển thị trên màn hình có DPI cao. Để ý kĩ, ta thấy chữ trên thanh tiêu đề vẫn rõ nét, trong khi các thành phần bên trong form đều bị mờ do Windows phóng to kích thước các thành phần này.

Giao diện Windows Forms bị mờ khi hiển thị trên màn hình DPI cao

Sau khi thực hiện một trong hai cách tôi trình bày bên dưới, ứng dụng hiển thị rõ nét như hình sau đây:

Lỗi giao diện Windows Forms đã được khắc phục

Cách 1: Dùng Interoperability

Với cách này, ta chỉ cần thêm vài dòng code đơn giản là giải quyết được vấn đề. Đầu tiên, ta mở form cần bật tính năng hiển thị DPI cao và chỉnh thuộc tính AutoScaleMode từ Font sang Dpi.

Windows Forms Scale Mode

Tiếp theo, ta mở file Program.cs, rồi thêm dòng khai báo sau vào trong class Program:

1
2
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();

Đoạn code này thông báo cho CLR biết là ta sẽ dùng hàm SetProcessDPIAware() nằm trong file user32.dll. Hàm này là một API của Windows. Để gọi nó, ta dùng kĩ thuật Interoperability, nghĩa là gọi code không được quản lý (unmanaged code) từ code được quản lý (managed code). Tiếp theo, ta thêm đoạn code sau vào trong Main():

1
2
3
4
if (Environment.OSVersion.Version.Major >= 6)
{
	SetProcessDPIAware();
}

Ở đây, tôi kiểm tra phiên bản Windows để chắc rằng nó từ Vista trở về sau, vì bản XP trở về trước không hỗ trợ DPI cao. Nếu hứng thú, bạn có thể tham khảo thêm thông tin về hàm này tại trang MSDN.

Vậy là xong, ứng dụng giờ đây trở nên rõ nét, các thành phần được canh chỉnh cho phù hợp với DPI của màn hình.

Cách 2: Dùng Application Manifest File

Cách này không sử dụng kĩ thuật Interoperability mà dùng file cấu hình app.manifest. Đầu tiên, ta thêm file này vào trong dự án bằng tùy chọn Add > New Item, tiếp theo ta chọn template Application Manifest File. Trong file này, Visual Studio đã để sẵn một loạt các cấu hình thông dụng nhưng được bỏ vào phần ghi chú. Ta kéo xuống gần cuối file để tìm đoạn code giống bên dưới và bỏ hai dấu ghi chú để kích hoạt nó.

<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
    </windowsSettings>
</application>

Cuối cùng, ta biên dịch lại ứng dụng và chạy thử để coi kết quả.

Lời kết

Tuy có thể tùy chỉnh Windows Forms để hiển thị tốt trên màn hình DPI cao, nhưng tôi khuyên bạn nên chuyển sang dùng WPF (Windows Presentation Foundation) vì ta không phải lo vấn đề DPI. Ngoài ra, gọi code không được quản lý trong một ứng dụng có code được quản lý luôn chứa những rủi ro về an toàn bảo mật. Do đó, tôi luôn né Interoperability ngoại trừ trường hợp bất khả kháng.