关闭

【WPF】DPI对控件定位产生的影响

标签: wpfdpi-界面
623人阅读 评论(1) 收藏 举报
分类:

需求

程序界面上是一个Window,当用户点击桌面上除此Window之外的任何地方,都要把这个window隐藏掉。程序有个托盘图标,点击托盘图标不能隐藏window,托盘上有个右键菜单,点击右键菜单也不能隐藏。

分析

1.系统像素与显示器像素

我们知道wpf中控件宽高的单位是1/96英寸,如果你系统的dpi为96(再这里我们不考虑显示器的dpi,那是windows系统的事情),那么1/96英寸就是1个系统像素。这里的像素要和显示器的像素分开。显示器的像素是出场的时候就已经设计好的,是不可改变的,如:1600(宽)x1200(高)。在这里我们可以把显示器的像素称为“像元”,系统的像素称为“系统像素”加以区分。像元是单个的发光物理单位,如一个发光二极管。如果我将此项改为800x600,则显示器显示的时候会将两个像元作为一个像素显示。

系统像素则是可以改变的,在win10中可以通过如下进行改变。
这里写图片描述

2.系统的dpi和wpf中的宽高单位

系统的dpi在win10中可以上图的更改文本、应用等项目的大小那里设置,默认的100%就是96dpi,当调成125%时,dpi就是96*125%=120dpi。此时,系统的分辨率还是不变的哦,仍然是1920x1200。

对于你的屏幕(1920x1200分辨率)在96dpi下,高度将是1200(一种与设备无关的单位)。在120dpi下,高度将是1200/(120/96)=960(与设备无关单位)。具体为什么,参考下面的Code1。总结:96dpi时,1设备无关单位=1像素,120dpi时,1设备无关单位=1像素x125%=1.25像素。所以如果你一个控件的width=100,在96dpi时显示是100个像素,在120dpi时将显示125个像素,这样看起来是不是就大了!!!

知道了这两点,就方便控件定位了。


(这里再插一句,系统分辨率和DPI间的关系:通过调小系统分辨率和调大DPI都可以实现放大系统文字和图标的功能,但是你会发现通过调整分辨率实现的放大会比较模糊,而调整DPI实现的放大几乎很少出现模糊的情况。这里的原因暂时还没有摸索出来,后续会继续更新)

3.需要获取鼠标的点击位置

鼠标点击位置的获取,可以通过下面代码Code2获得,单位是系统像素。

4.获取到的鼠标位置与设备无关单位间的的转换

鼠标位置的单位是系统像素,假如鼠标距屏幕左边是100像素吧。则100/(系统的dpi/96)=?得到的值就是一个与设备无关的左边距的值,也就是与wpf中控件的width,height同样的单位。


Code1:系统像素,系统dpi的获取方法:

using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
{
     Console.WriteLine("xDpi:" + g.DpiX + " YDPI:" + g.DpiY);
}
Console.WriteLine("【PrimaryScreen.Bounds(系统设置中像素的值)】");
Console.WriteLine(string.Format("width{0},height{1}", System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height));
Console.WriteLine("【设备宽高(dpi)】");
Console.WriteLine(string.Format("Width:{0},Height:{1}", SystemParameters.PrimaryScreenWidth, SystemParameters.PrimaryScreenHeight));
Console.WriteLine("【WorkAera宽高(dpi)】");
Console.WriteLine(string.Format("Width:{0},Height:{1}", SystemParameters.WorkArea.Width, SystemParameters.WorkArea.Height));

每次更改dpi后,必须注销系统才能获取到新的dpi。
(从上面代码中,可以看到无论你dpi怎么改,系统像素是一直不变的。设备的宽高和workarea的宽高会改变)


Code2:鼠标位置获取

 class MouseHook
    {
        private const int WM_MOUSEMOVE = 0x200;
        private const int WM_LBUTTONDOWN = 0x201;
        private const int WM_RBUTTONDOWN = 0x204;
        private const int WM_MBUTTONDOWN = 0x207;
        private const int WM_LBUTTONUP = 0x202;
        private const int WM_RBUTTONUP = 0x205;
        private const int WM_MBUTTONUP = 0x208;
        private const int WM_LBUTTONDBLCLK = 0x203;
        private const int WM_RBUTTONDBLCLK = 0x206;
        private const int WM_MBUTTONDBLCLK = 0x209;

        public event MouseEventHandler OnMouseActivity;

        static int hMouseHook = 0;

        public const int WH_MOUSE_LL = 14;

        HookProc MouseHookProcedure;
        Log _log = new Log("MouseHook", true, Log4netWrapper.Default);
        [StructLayout(LayoutKind.Sequential)]
        public class POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public class MouseHookStruct
        {
            public POINT pt;
            public int hWnd;
            public int wHitTestCode;
            public int dwExtraInfo;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);


        public MouseHook()
        {
        }

        ~MouseHook()
        {
            Stop();
        }

        public void Start()
        {
            if (hMouseHook == 0)
            {
                MouseHookProcedure = new HookProc(MouseHookProc);

                hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure, Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]), 0);

                if (hMouseHook == 0)
                {
                    Stop();
                    _log.E("SetWindowsHookEx failed.");
                }
            }
        }

        public void Stop()
        {
            bool retMouse = true;
            if (hMouseHook != 0)
            {
                retMouse = UnhookWindowsHookEx(hMouseHook);
                hMouseHook = 0;
            }

            if (!(retMouse))
            {
                _log.E("UnhookWindowsHookEx failed.");
            }
        }

        private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            if ((nCode >= 0) && (OnMouseActivity != null))
            {
                MouseButtons button = MouseButtons.None;
                int clickCount = 0;

                switch (wParam)
                {
                    case WM_LBUTTONDOWN:
                        button = MouseButtons.Left;
                        clickCount = 1;
                        break;
                    case WM_LBUTTONUP:
                        button = MouseButtons.Left;
                        clickCount = 1;
                        break;
                    case WM_LBUTTONDBLCLK:
                        button = MouseButtons.Left;
                        clickCount = 2;
                        break;
                    case WM_RBUTTONDOWN:
                        button = MouseButtons.Right;
                        clickCount = 1;
                        break;
                    case WM_RBUTTONUP:
                        button = MouseButtons.Right;
                        clickCount = 1;
                        break;
                    case WM_RBUTTONDBLCLK:
                        button = MouseButtons.Right;
                        clickCount = 2;
                        break;
                }

                MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
                MouseEventArgs e = new MouseEventArgs(button, clickCount, MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y, 0);

                OnMouseActivity(this, e);
            }
            return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
        }
    }

使用方法:

MouseHook hook = new MouseHook();
hook.OnMouseActivity += Hook_OnMouseActivity;
hook.Start();

在Hook_OnMouseActivity中可以获取到鼠标的位置。

实现

这里就不用再赘述代码了,只有关键的一点,在程序中,判断与鼠标点击位置与窗体显示位置之间的距离,要做差运算,所以单位要统一。我建议将鼠标的像素位置值转为与设备无关的值,然后进行计算,否则会出现偏移。

1
0
查看评论

wpf在获得在不同dpi下的x,y对应的x,y坐标

[DllImport("User32.dll")] private static extern IntPtr GetDC(HandleRef hWnd); [DllImport("User32.dll")] ...
  • ksr12333
  • ksr12333
  • 2016-02-12 11:14
  • 1099

关于WPF中宽高分辨率无关的理解

首先看几个概念: DPI:指的是
  • yulongguiziyao
  • yulongguiziyao
  • 2014-10-11 17:28
  • 1554

WPF改变图片的DPI

 改变图片的DPI,比如将DPI是96的图片改变为DPI是147的图片,我们采用的方法是创建一个新的BitmapSource,将原来图片的内容拷贝过来。代码如下: targetBitmap.CopyPixels(pixelData, width * 4, 0); BitmapSo...
  • Libby1984
  • Libby1984
  • 2016-09-27 10:16
  • 596

WPF分辨率无关性应用

  • 2011-04-15 13:30
  • 100KB
  • 下载

WPF 分辨率无关性原理

WPF在计算窗口尺寸大小时使用的是系统的DPI设置。WPF窗口以及窗口中所有的元素都是使用设备无关单位度量。一个设备无关单位被定义为1/96英寸。   [物理单位尺寸]=[设备无关单位尺寸]*[系统DPI]=1/96英寸*96dpi=1像素。   当系统默认dpi设置为96dpi时,也就是需要9...
  • imJaron
  • imJaron
  • 2017-04-13 10:55
  • 394

再论WPF中的UseLayoutRounding和SnapsToDevicePixels

最近在调试项目中的UI时发现几个诡异问题: 1.Grid容器里的GridSplitter设置粗细一样, 但截屏放大后发现线条不一样粗并且明暗不一致,导致打印出来有问题。 2.自定义控件的边缘在某些窗体中显示模糊。 导致以上问题的原因是:1)UserLayoutRounding为False,导致控...
  • muzizongheng
  • muzizongheng
  • 2013-11-05 11:16
  • 5395

WPF中如何使webbrowser适应DPI

WPF中,webbrowser本身是有一些缺陷的,不能自适应DPI,具体原因,以后再补充。先贴代码及如何使用。(很不负责的先贴代码。。。有时间再写清楚) 做法是:先加载一个空的HTML,当该HTML加载完后,进行webbrowser的zoom,然后,再去加载你要加载的URL。 nam...
  • woyaowenzi
  • woyaowenzi
  • 2012-03-02 19:15
  • 3927

【C#/WPF】修改图像的DPI、Resolution

Bitmap.SetResolution
  • qq_18995513
  • qq_18995513
  • 2017-04-21 17:09
  • 1430

使用colorbox进行弹窗遇到的怪事,关闭弹窗报错colorbox undefined

今天在做前端一个弹窗的功能的时候出现了一个bug。 弹窗用的是colorboxkongj
  • smok56888
  • smok56888
  • 2014-05-05 17:30
  • 1019

B2B电子商务与企业供应链管理

电子商务发展的真正突破是B2B的电子商务,即企业对企业的电子商务,是在上下游企业之间从事的网络商务活动,是网络经济的基础。从参与企业的数量、涉及的金额、交互信息的规模上来说,B2B都将成为电子商务的主体。在这种环境下,企业不仅要协调企业内计划、采购、制造、销售的各个环节,还要与包括供应商、承销商等在...
  • zhaoyang17
  • zhaoyang17
  • 2004-09-30 17:58
  • 1554
    个人资料
    • 访问:206181次
    • 积分:2788
    • 等级:
    • 排名:第14688名
    • 原创:90篇
    • 转载:18篇
    • 译文:1篇
    • 评论:36条
    文章分类
    最新评论