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
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM