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

Проблема

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

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

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

Задний план

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

Правки

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

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

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


person greenoldman    schedule 20.10.2010    source источник


Ответы (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));
    }
}
person mbursill    schedule 20.10.2010
comment
Спасибо, но это половина проблемы - теперь у меня координаты экрана в пикселях, а не в единицах WPF. Я не могу использовать PointFromScreen, потому что для этого мне нужен какой-то виджет. Итак, как преобразовать пиксели в единицы, имея только рабочий стол? Есть ли какой-нибудь способ для этого? - person greenoldman; 21.10.2010
comment
Я добавил код, чтобы показать, как пиксели могут быть преобразованы в единицы WPF без необходимости иметь уже отображаемое окно. Существует также встроенный метод WPF (на любом визуальном элементе WPF), называемый PointFromScreen, однако для его работы необходимо иметь прикрепленный визуальный элемент. - person mbursill; 21.10.2010
comment
Point wpfUnits = new Point(physicalUnitSize * (double)x, physicalUnitSize * (double)y); следует заменить на Point wpfUnits = new Point((double)x / physicalUnitSize, (double)y / physicalUnitSize);, чтобы исправить масштабирование экрана. - person Christian Beregula; 17.06.2020

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

Используйте 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, ознакомьтесь с дополнительной информацией о второй на pinvoke.net.

person Community    schedule 20.10.2010
comment
Спасибо, я использую второй маршрут, потому что он не требует добавления новых ссылок, я использовал код, представленный mbursill. - person greenoldman; 21.10.2010

Я наткнулся на эту тему, ища решение той же проблемы. Тем временем я нашел 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);
}
person Christoph    schedule 03.07.2016