wpf单位转换及DPI获取

wpf使用设备无关单位,一开始的理解是无论DPI怎么变,显示的窗口大小是一样的,实际效果窗口会随着系统DPI缩放(哪里错了?请大佬明示);wpf设备无关单位转换成物理单位是与DPI无关的,下面给出一个Dpi结构:

public struct Dpi {
    public Dpi (Double x, Double y) {
        DpiX = x;
        DpiY = y;

        Px2WpfX = 96 / DpiX;
        Px2WpfY = 96 / DpiY;
    }

    public Double DpiX { get; }

    public Double DpiY { get; }

    public Double Px2WpfX { get; }

    public Double Px2WpfY { get; }

    /// <summary>
    /// 英寸-厘米
    /// </summary>
    public static readonly Double In2Cm = 2.54;
    /// <summary>
    /// 英寸-磅
    /// </summary>
    public static readonly Double In2Pt = 72;
    /// <summary>
    /// 厘米-wpf
    /// </summary>
    public static readonly Double Cm2Wpf = 96 / 2.54;
}

可以看到,wpf设备无关单位转厘米的比例是固定的,继而又可以转换成英寸、磅等,只有换算成像素才需要DPI;下面说一下如何获取DPI。

.net core提供了VisualTreeHelper.GetDpi(Visual visual)方法,返回DpiScale结构,PixelsPerInchX表示1英寸像素数(即DPI),DpiScaleX表示1个wpf设备无关单位可以转换成多少像素;并且Window、Image等对象还提供了DpiChanged事件,非常方便根据DPI变化调整大小;

.net framework 4.8以下没有上述便利得方法;获取dpi的方法参考https://blog.csdn.net/Iron_Ye/article/details/83053393,里面说了多种获取DPI的方法,并且向我们推荐使用CompositionTarget方式,经测试(本人Win10),改变系统显示比例150%后,除Win32API外,其他方法获取的DPI与显示比列为100%无异,而Win32API获取的DPI与.net core使用VisualTreeHelper.GetDpi()一样,所以得出结论,若要获取准确的DPI,推荐使用Win32API,每次传入窗口句柄或Intptr.Zero,我的猜想是这样的(没有去验证,我只有一块屏幕),传入窗口句柄则获取窗口句柄所在屏幕的DPI,传入Intptr.Zero则相当于传入空指针,即获取主屏幕DPI;另外CompositionTarget需要注意一下,PresentationSource.FromVisual(visual)传入DrawingVisual会返回null,需要传入DrawingVisual所在的逻辑树结构对象(一般是Canvas);与Dpi结构配合,这里给出DpiHelper类示范:

public sealed class DpiHelper {
    #region Graphics

    public static Dpi GetDpiByGraphics (IntPtr hWnd) {
        using (var graphics = Graphics.FromHwnd (hWnd)) {
            return new Dpi (graphics.DpiX, graphics.DpiY);
        }
    }

    #endregion

    #region CompositionTarget

    public static Dpi GetDpiFromVisual (Visual visual) {
        var source = PresentationSource.FromVisual (visual);
        return (source == null || source.CompositionTarget == null) ? GetDpiByWin32 (IntPtr.Zero) : new Dpi (96.0 * source.CompositionTarget.TransformToDevice.M11, 96.0 * source.CompositionTarget.TransformToDevice.M22);
    }

    #endregion

    #region Win32 API

    private const Int32 LOGPIXELSX = 88;
    private const Int32 LOGPIXELSY = 90;

    [DllImport ("gdi32.dll")]
    private static extern Int32 GetDeviceCaps (IntPtr hdc, Int32 index);

    [DllImport ("user32.dll")]
    private static extern IntPtr GetDC (IntPtr hWnd);

    [DllImport ("user32.dll")]
    private static extern Int32 ReleaseDC (IntPtr hWnd, IntPtr hDc);

    public static Dpi GetDpiByWin32 (IntPtr hwnd) {
        var hDc = GetDC (hwnd);

        var dpiX = GetDeviceCaps (hDc, LOGPIXELSX);
        var dpiY = GetDeviceCaps (hDc, LOGPIXELSY);

        ReleaseDC (hwnd, hDc);
        return new Dpi (dpiX, dpiY);
    }

    #endregion
}

 

posted @ 2020-09-25 11:01  孤独成派  阅读(1774)  评论(0编辑  收藏  举报