How to get mouse position related to desktop in WPF?

12,061

Getting the Screen Coordinates:

[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));
    }
}

Converting Pixels to WPF Units:

[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;          
}

Putting both together:

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));
    }
}
Share:
12,061
greenoldman
Author by

greenoldman

I am interested in such topics: usability and accessibility of user interfaces, design of programming languages, natural language processing, machine translation, data mining. Currently I am developing compiler and programming language Skila -- read more at aboutskila.wordpress.com.

Updated on July 27, 2022

Comments

  • greenoldman
    greenoldman over 1 year

    Problem

    When you search for such question using google you get a lot of hits but all solutions assume you have at least one window.

    But my question is just like I phrased it -- not assumptions at all. I can have a window, but I could have zero windows (because I didn't even show one or I just closed the last one). So in short the solution cannot rely on any widget or window -- the only thing is known, is there is a desktop (and app running, but it does not have any windows).

    So the question is -- how to get the mouse position?

    Background

    I would like to show windows centered to mouse position. There is no such mode in WPF (there are only center to owner, or center to screen) so I have to do it manually. The missing piece is mouse position.

    Edits

    Thank you all, so now I have the first part of the solution -- raw position. Now there is a problem how to convert the data for WPF. I found such topic: WPF Pixels to desktop pixels but again, it assumes having some window.

    Then I googled more and I found solution: http://jerryclin.wordpress.com/2007/11/13/creating-non-rectangular-windows-with-interop/

    the code includes class for scaling up/down coordinates relying only on info about desktop. So joining those two pieces, I finally get the solution :-). Thanks again.

  • greenoldman
    greenoldman about 13 years
    Thank you, but this is half of the problem -- now I have screen coordinates in pixels, not in WPF units. I cannot use PointFromScreen because for that I need some widget. So how to convert pixels to units having only desktop? Is there any method for it?
  • mbursill
    mbursill about 13 years
    I added code to show how the pixels can be converted to WPF units, without the need to have a Window already shown. There is also a built in WPF method (on any WPF Visual) called PointFromScreen, however, you need to have an attached visual for that to work.
  • Christian Beregula
    Christian Beregula over 3 years
    Point wpfUnits = new Point(physicalUnitSize * (double)x, physicalUnitSize * (double)y); should be replaced to Point wpfUnits = new Point((double)x / physicalUnitSize, (double)y / physicalUnitSize); to fix screen scaling