Как получить положение мыши, связанное с рабочим столом в WPF?

проблема

При поиске такого вопроса с помощью Google вы получаете много просмотров, но все решения предполагают, что у вас есть хотя бы одно окно.

Но мой вопрос такой же, как я его сформулировал, а не предположения. У меня может быть окно, но у меня может быть ноль окон (потому что я даже не показывал одно или просто закрыл последнее). Короче говоря, решение не может полагаться на какой-либо виджет или окно - известно только то, что есть рабочий стол (и приложение работает, но у него нет окон).

Итак, вопрос - как получить положение мыши?

Фон

Я хотел бы показать окна по центру позиции мыши. В WPF такого режима нет (есть только центр по владельцу или центр по экрану), поэтому я должен сделать это вручную. Недостающим элементом является позиция мыши.

Правки

Спасибо всем, так что теперь у меня есть первая часть решения - грубая позиция. Теперь возникает проблема, как конвертировать данные для WPF. Я нашел такую ​​тему: WPF Pixels для настольных пикселей, но опять же, предполагает наличие некоторого окна.

Потом я погуглил еще и нашел решение: http://jerryclin.wordpress.com/2007/11/13/creating-non-rectangular-windows-with-interop/

код включает в себя класс для масштабирования координат вверх / вниз, опираясь только на информацию о рабочем столе. Итак, соединив эти две части, я наконец-то нашел решение:-). Еще раз спасибо.

3 ответа

Решение

Получение координат экрана:

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

Два варианта:

использование 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 для вас. Я не совсем уверен, что для этого нужно, чтобы у вас был всплеск интерфейса, но я так не думаю. Да, это winforms, а не wpf, но он действительно не имеет никакого отношения к тому, где он находится.

Если вы хотите пропустить какие-либо зависимости от system.windows.forms.dll, ознакомьтесь с дополнительной информацией о секунде на pinvoke.net.

Я наткнулся на эту тему, ища решение той же проблемы. А пока я нашел PointToScreen, который не требует никакого P/Invoke. Метод доступен на любом Visual запуск.NET 3.0 (и, таким образом, UIElement, Controlи т. д.) и реализация будет выглядеть так:

protected void OnMouseLeave(object Sender, MouseEventArgs e) {
    var relativePosition = e.GetPosition(this);
    var screenPosition = this.PointToScreen(relativePosition);
}
Другие вопросы по тегам