在高DPI Windows应用程序中使用DEFAULT_GUI_FONT

13z8s7eq  于 2023-08-07  发布在  Windows
关注(0)|答案(3)|浏览(111)

我有一个Windows应用程序,我想在高DPI显示器上看起来很好。应用程序在很多地方使用DEFAULT_GUI_FONT,并且以这种方式创建的字体不能正确缩放。
有没有简单的方法来解决这个问题,而不是太多的痛苦?

8yparm6h

8yparm6h1#

你需要通过SystemParametersInfoSPI_GETNONCLIENTMETRICS,)得到NONCLIENTMETRICS,然后使用它的LOGFONT数据,用于创建自己的字体。也可以查询SystemParametersInfoSPI_GETICONTITLELOGFONT)并使用它

du7egjpx

du7egjpx2#

不同用途的推荐字体可以从NONCLIENTMETRICS结构中获得。
对于自动DPI缩放的字体(Windows 10 1607+,必须支持每显示器DPI):

// Your window's handle
HWND window;

// Get the DPI for which your window should scale to
const UINT dpi{GetDpiForWindow(window)};

// Obtain the recommended fonts, which are already correctly scaled for the current DPI
NONCLIENTMETRICSW non_client_metrics;
non_client_metrics.cbSize = sizeof(non_client_metrics);

if (!SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, sizeof(non_client_metrics), &non_client_metrics, 0, dpi)
{
    // Error handling
}

// Create an appropriate font(s)
HFONT message_font{CreateFontIndirectW(&non_client_metrics.lfMessageFont)};

if (!message_font)
{
    // Error handling
}

字符串
对于较旧的Windows版本,您可以使用系统范围的DPI并手动缩放字体(Windows 7+,必须支持系统DPI):

// Your window's handle
HWND window;

// Obtain the recommended fonts, which are already correctly scaled for the current DPI
NONCLIENTMETRICSW non_client_metrics;
non_client_metrics.cbSize = sizeof(non_client_metrics);

if (!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(non_client_metrics), &non_client_metrics, 0)
{
    // Error handling
}

// Get the system-wide DPI
HDC hdc{GetDC(nullptr)};

if (!hdc)
{
    // Error handling
}

const UINT dpi{GetDeviceCaps(hdc, LOGPIXELSY)};
ReleaseDC(nullptr, hdc);

// Scale the font(s)
constexpr UINT font_size{12};
non_client_metrics.lfMessageFont.lfHeight = -((font_size * dpi) / 72);

// Create the appropriate font(s)
HFONT message_font{CreateFontIndirectW(&non_client_metrics.lfMessageFont)};

if (!message_font)
{
    // Error handling
}


NONCLIENTMETRICS也有许多其他字体。请确保选择适合您的目的。
您应该在application manifest中设置DPI感知级别,如here所述,以获得最佳兼容性。

a0x5cqrl

a0x5cqrl3#

.NET框架中的WinForms内部转换DEFAULT_GUI_FONT(实际上在大多数情况下用于获得WinForms Forms和控件的默认字体),通过将其高度从像素(GDI字体本机使用的单位)缩放到Points(GDI+更喜欢)。使用点绘制文本意味着渲染文本的物理大小取决于显示器DPI设置。

System.Drawing.Font.SizeInPoints:

float emHeightInPoints;
                    
IntPtr screenDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef);

try {
    using( Graphics graphics = Graphics.FromHdcInternal(screenDC)){
        float pixelsPerPoint      = (float) (graphics.DpiY / 72.0);
        float lineSpacingInPixels = this.GetHeight(graphics);
        float emHeightInPixels    = lineSpacingInPixels * FontFamily.GetEmHeight(Style)  / FontFamily.GetLineSpacing(Style);
        
        emHeightInPoints    = emHeightInPixels / pixelsPerPoint;
    }
}
finally {
    UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, screenDC));
}

return emHeightInPoints;

字符串
显然,你不能直接使用它,因为它是C#。但除此之外,this article建议您应该缩放像素尺寸,假设设计为96 dpi,并使用GetDpiForWindow来确定实际的DPI。请注意,上面公式中的“72”与显示器DPI设置无关,它来自于.NET喜欢使用点而不是像素指定的字体(否则只需按DPIy/96缩放LOGFONT的高度)。
This site表示类似的东西,但使用GetDpiForMonitor
我不能肯定地说,根据一些DPI依赖的因素手动缩放字体大小的一般方法是否是一个强大的和未来的缩放字体(这似乎是去缩放非字体GUI元素的方式)。然而,由于.NET基本上也只是基于某种DPI值计算一些神奇的因子,这可能是一个很好的猜测。
此外,您还需要缓存HFONTHFONT-LOGFONT转换不可忽略。
参见(参考文献):
WinForms默认使用GetStockObject(DEFAULT_GUI_FONT)(但也有一些例外,大部分是过时的):

IntPtr handle = UnsafeNativeMethods.GetStockObject(NativeMethods.DEFAULT_GUI_FONT);        
try {
    Font fontInWorldUnits = null;

    // SECREVIEW : We know that we got the handle from the stock object,
    //           : so this is always safe.
    //
    IntSecurity.ObjectFromWin32Handle.Assert();
    try {
        fontInWorldUnits = Font.FromHfont(handle);
    }
    finally {
        CodeAccessPermission.RevertAssert();
    }

    try{
        defaultFont = FontInPoints(fontInWorldUnits);
    }
    finally{
        fontInWorldUnits.Dispose();
    }
}
catch (ArgumentException) {
}


https://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/SystemFonts.cs,355
HFONT转换为GDI+,然后使用FontInPoints转换以这种方式检索的GDI+字体:

private static Font FontInPoints(Font font) {
    return new Font(font.FontFamily, font.SizeInPoints, font.Style, GraphicsUnit.Point, font.GdiCharSet, font.GdiVerticalFont);
}


https://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/SystemFonts.cs,452
SizeInPoints getter的内容已经在上面列出了。
https://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Advanced/Font.cs,992

相关问题