如何在WPF中获取与桌面相关的鼠标位置?

8

问题

当你使用谷歌搜索这样的问题时,你会得到很多结果,但所有的解决方案都假设你至少有一个窗口。

但我的问题就像我表述的那样——完全没有假设。我可以有一个窗口,但我也可能没有窗口(因为我甚至没有展示一个或者我刚关闭了最后一个)。所以简而言之,解决方案不能依赖于任何小部件或窗口——唯一已知的是有一个桌面(和运行的应用程序,但它没有任何窗口)。

所以问题是——如何获取鼠标位置?

背景

我想显示居中于鼠标位置的窗口。在WPF中没有这样的模式(只有居中于所有者或居中于屏幕),所以我必须手动实现。缺失的部分就是鼠标位置。

编辑

谢谢大家,现在我有了解决方案的第一部分——原始位置。现在的问题是如何将数据转换为WPF。我找到了这样的主题:WPF像素到桌面像素,但它再次假设有一些窗口。

然后我继续谷歌搜索,我找到了解决方案:http://jerryclin.wordpress.com/2007/11/13/creating-non-rectangular-windows-with-interop/

代码包括一个类,用于缩放仅依赖于桌面信息的坐标。将这两个部分结合起来,我最终得到了解决方案:-)。再次感谢。

3个回答

21

获取屏幕坐标:

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);

[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public int X;
    public int Y;

    public POINT(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }
}

private void WritePoint(object sender, RoutedEventArgs e)
{
    POINT p;
    if (GetCursorPos(out p))
    {
        System.Console.WriteLine(Convert.ToString(p.X) + ";" + Convert.ToString(p.Y));
    }
}

将像素转换为WPF单位:

[DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);

[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);

private Point ConvertPixelsToUnits(int x, int y)
{
    // get the system DPI
    IntPtr dDC = GetDC(IntPtr.Zero); // Get desktop DC
    int dpi = GetDeviceCaps(dDC, 88);
    bool rv = ReleaseDC(IntPtr.Zero, dDC);

    // WPF's physical unit size is calculated by taking the 
    // "Device-Independant Unit Size" (always 1/96)
    // and scaling it by the system DPI
    double physicalUnitSize = (1d / 96d) * (double)dpi;
    Point wpfUnits = new Point(physicalUnitSize * (double)x,
        physicalUnitSize * (double)y);

    return wpfUnits;          
}

把两者结合起来:

private void WriteMouseCoordinatesInWPFUnits()
{
    POINT p;
    if (GetCursorPos(out p))
    {
        Point wpfPoint = ConvertPixelsToUnits(p.X, p.Y);
        System.Console.WriteLine(Convert.ToString(wpfPoint.X) + ";" + Convert.ToString(wpfPoint.Y));
    }
}

谢谢,但这只解决了问题的一半 - 现在我有以像素为单位的屏幕坐标,而不是WPF单位。我无法使用PointFromScreen,因为它需要一些小部件。那么如何将像素转换为只有桌面的单位?是否有任何方法可以实现? - greenoldman
我添加了代码以展示如何将像素转换为WPF单位,而无需先显示窗口。此外,还有一个内置的WPF方法(在任何WPF可视化对象上)叫做PointFromScreen,但是需要有一个附加的可视化对象才能使用它。 - mbursill
1
应该将 Point wpfUnits = new Point(physicalUnitSize * (double)x, physicalUnitSize * (double)y); 替换为 Point wpfUnits = new Point((double)x / physicalUnitSize, (double)y / physicalUnitSize); 以修复屏幕缩放问题。 - Christian Beregula

4

两种选择:

使用System.Windows.Forms.Control.MousePosition,或者进行p/invoke调用。

[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
public static extern bool GetCursorPos([In, Out] NativeMethods.POINT pt);

第一个选项已经为您执行了p/invoke。我不确定它是否需要您有一些UI闪现,但我认为不需要。是的,它是winforms而不是wpf,但这与其所在的位置无关。
如果您想跳过对system.windows.forms.dll的任何依赖项,请查看{{link1:有关pinvoke.net上第二个选项的更多信息}}。

谢谢,我使用第二条路线,因为它不需要添加新的引用,我使用了mbursill提供的代码。 - greenoldman

0

我在寻找同样问题的解决方案时,偶然看到了那个帖子。同时,我找到了 PointToScreen,它不需要任何 P/Invoke。该方法可在任何 .NET 3.0(因此包括 UIElementControl 等)上使用,实现将类似下面这样:

protected void OnMouseLeave(object Sender, MouseEventArgs e) {
    var relativePosition = e.GetPosition(this);
    var screenPosition = this.PointToScreen(relativePosition);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接