Alexandru
Alexandru

Reputation: 12912

What is the best way to show a WPF window at the mouse location (to the top left of the mouse)?

I have found that this works PART of the time by inheriting the Windows Forms mouse point and subtracting out the height and width of my window to set the left and top (since my window's size is fixed):

MyWindowObjectThatInheritsWindow window = new MyWindowObjectThatInheritsWindow();
System.Windows.Point mouseLocation = GetMousePositionWindowsForms();
window.Left = mouseLocation.X - 300;
window.Top = mouseLocation.Y - 240;
window.Show();

Edit: Here is the code for getting the mouse position...

public System.Windows.Point GetMousePositionWindowsForms()
{
    System.Drawing.Point point = System.Windows.Forms.Control.MousePosition;
    return new System.Windows.Point(point.X, point.Y);
}

Note that this works by making the bottom right edge of the window touch the top left of your mouse cursor. But this breaks for different screen resolutions, or maybe multiple monitors with different resolutiosn? I haven't fully narrowed it down yet, but I just tried this same code on another PC, and it seems to spawn the window not to the top left of the mouse cursor, but to the bottom left of it, and a good distance past it...

I should probably add that my window sizes to content, width and height, so I can't just use the ActualWidth and ActualHeight properties since they're not available. Perhaps the issue is in getting that sizing right? Is there any way to do that? I know for sure the 300 and 240 is correct according to my main PC with two monitors running 1920x1080 resolutions, as I have calculated the widths and heights of all the objects in my window which I have explicitly sized. Edit: Just tried explicitly setting the height and width to 240/300, to ensure that the window is no longer sized to content, and I still have this issue when subtracting out the actual height and width!

Any ideas?

Upvotes: 10

Views: 18265

Answers (5)

livan3li
livan3li

Reputation: 879

As @Gordon Slysz commented Alexandru's answer:

I had better luck with hooking things up to the 'Loaded' event; I found there was a flashing effect when the dialog is moved from its initial rendered position, if I used the OnContentRendered

I'm posting this answer since I couldn't edit Alexandru's answer.

Loaded event didnt work for me and with some research I found this. According to this article, I tried SourceInitialized event and result is good. It doesnt do flashing effect as Gordon describes and directly is rendered cursor's position.

Upvotes: 0

Youngwook Jun
Youngwook Jun

Reputation: 81

Also you can do as below:

private void Button_Click(object sender, RoutedEventArgs e)
{
    ConfigWindow cw = new ConfigWindow();
    var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
    var mouse = transform.Transform(GetMousePosition());
    cw.WindowStartupLocation = WindowStartupLocation.Manual;
    cw.Left = mouse.X - cw.ActualWidth;
    cw.Top = mouse.Y - cw.ActualHeight;
    cw.Show();
}

public System.Windows.Point GetMousePosition()
{
    System.Drawing.Point point = System.Windows.Forms.Control.MousePosition;
    return new System.Windows.Point(point.X, point.Y);
}

Upvotes: 0

Jan Gassen
Jan Gassen

Reputation: 3534

You can also do this by slightly modifying your initial example and positioning the window before showing it.

MyWindowObjectThatInheritsWindow window = new MyWindowObjectThatInheritsWindow();

var helper = new WindowInteropHelper(window);
var hwndSource = HwndSource.FromHwnd(helper.EnsureHandle());
var transformFromDevice = hwndSource.CompositionTarget.TransformFromDevice;

System.Windows.Point wpfMouseLocation = transformFromDevice.Transform(GetMousePositionWindowsForms());
window.Left = wpfMouseLocation.X - 300;
window.Top = wpfMouseLocation.Y - 240;
window.Show();

Upvotes: 2

Alexandru
Alexandru

Reputation: 12912

In the end, this did the trick:

        protected override void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);
            MoveBottomRightEdgeOfWindowToMousePosition();
        }

        private void MoveBottomRightEdgeOfWindowToMousePosition()
        {
            var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
            var mouse = transform.Transform(GetMousePosition());
            Left = mouse.X - ActualWidth;
            Top = mouse.Y - ActualHeight;
        }

        public System.Windows.Point GetMousePosition()
        {
            System.Drawing.Point point = System.Windows.Forms.Control.MousePosition;
            return new System.Windows.Point(point.X, point.Y);
        }

Upvotes: 18

Sheridan
Sheridan

Reputation: 69959

Can you not use something like this?:

Point mousePositionInApp = Mouse.GetPosition(Application.Current.MainWindow);
Point mousePositionInScreenCoordinates = 
    Application.Current.MainWindow.PointToScreen(mousePositionInApp);

I haven't been able to test it, but I think it should work.


UPDATE >>>

You don't have to use the Application.Current.MainWindow as the parameter in these methods... it should still work if you have access to a Button or another UIElement in a handler:

Point mousePositionInApp = Mouse.GetPosition(openButton);
Point mousePositionInScreenCoordinates = openButton.PointToScreen(mousePositionInApp);

Again, I haven't been able to test this, but if that fails as well, then you can find one more method in the How do I get the current mouse screen coordinates in WPF? post.

Upvotes: 4

Related Questions