10

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?

Alexandru
  • 12,264
  • 17
  • 113
  • 208
  • It is not clear what you want exactly, not even what your code does (what is exactly returning GetMousePositionWindowsForms?). Can you please be a bit clearer? – varocarbas Nov 04 '13 at 14:20
  • No problem. In a nutshell: I want to set a WPF window's bottom right edge to the top left of the mouse cursor. I have added in the function you requested. – Alexandru Nov 04 '13 at 14:22
  • The problem is more or less clear; but your code not too much: it seems to be a mixture of C# winforms and WPF. In any case, I guess that you can continue this discussion with the answerer you got. – varocarbas Nov 04 '13 at 14:43
  • @varocarbas Yes, it uses WinForms to get the mouse point on screen, but unfortunately that answer I got does not seem to work in my case since my MainWindow is hidden since my application consists of just a system tray icon. – Alexandru Nov 04 '13 at 14:48
  • I am not familiar with this kind of mixtures (usually, you get directly anything you want in WPF or create a winforms application). Might not this be provoking the problems (wrongly coordinating two different frameworks)? In any case, just explain the exact conditions to Sheridan such that he can come up with a solution working for you. – varocarbas Nov 04 '13 at 14:53
  • @varocarbas Perhaps it can be a problem to mix like this, I am not sure, but I wanted to leverage the power of WPF for creating nicer content in my application instead of using WinForms for the windows of the application, which is why I mixed it like this. There must be a way to do what I want. I wish it were simpler to get the current mouse point on screen, but even if you Google how to get the cursor position on screen, its tricky no matter how you do it in WPF, and using the Windows forms method seems to be the easiest way for me. – Alexandru Nov 04 '13 at 14:56
  • I am used to winforms and for me is also easier in winforms; that does not mean that there isn't an easy way in WPF. Usually, mixing up so different things tends to drive to unreliable situations. In your position, I would firstly make sure that I get what I want working exactly as I want in one format (the one I am more comfortable with); and only then would think about adding bits from a different format. – varocarbas Nov 04 '13 at 15:19
  • @varocarbas Check out my posted answer. That seems to work reliably. – Alexandru Nov 05 '13 at 13:16

4 Answers4

17

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);
        }
Alexandru
  • 12,264
  • 17
  • 113
  • 208
  • 2
    Works well. 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 – Gordon Slysz Jun 16 '17 at 18:11
4

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.

Community
  • 1
  • 1
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • I have tried this, but I get this error out of it: An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationCore.dll Additional information: This Visual is not connected to a PresentationSource. – Alexandru Nov 04 '13 at 14:26
  • Ahhh, sorry. In the constructor of `MainWindow.xaml.cs`, try adding this: `Application.Current.MainWindow = this;`. If that still doesn't work, try deferring the call to the above code... see [this post](http://stackoverflow.com/questions/2154211/in-wpf-under-what-circumstances-does-visual-pointfromscreen-throw-invalidoperat) for more information. – Sheridan Nov 04 '13 at 14:33
  • Ah, actually that won't work because my MainWindow is hidden. I made a WPF application that creates a taskbar icon and creates a hidden window off the bat, so I still see this error. Actually, the code above is from MainWindow.cs so I just tried using the "this" object but, unfortunately no luck. Here is the MainWindow XAML: – Alexandru Nov 04 '13 at 14:38
  • I'm not sure that I understand... what `Window` are you using for your application then? – Sheridan Nov 04 '13 at 14:48
  • Good question. I don't use a window. I create a system tray icon in the system tray. When the user clicks it, an event handler fires and creates a window for the user interface of the application (which is actually not MainWindow). – Alexandru Nov 04 '13 at 14:49
  • Here is the code I use to create a system tray icon on the MainWindow constructor (sorry, did I say taskbar before? I meant system tray): SystemTrayIcon = new System.Windows.Forms.NotifyIcon(); SystemTrayIcon.Icon = new System.Drawing.Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream("Executioner.icon.ico")); SystemTrayIcon.Click += notifyIcon_Click; SystemTrayIcon.Visible = true; – Alexandru Nov 04 '13 at 14:50
  • I guess this complicates things a lot...not sure why it doesn't work on my other machine with the same code though :( – Alexandru Nov 04 '13 at 16:51
  • I tried to put your code edit into the constructor of the new window, but even when I try getting a UI element like a button or a textbox, I still get the same error as before about not being connected to a presentation source. Any ideas why that's happening? By the way, thanks for your help so far! – Alexandru Nov 04 '13 at 17:08
  • I'm not really sure, but I think that it's because it hasn't been set up yet... like trying to access elements in a `ControlTemplate` before it's been applied. You might have to defer/delay running that code, eg. *don't* do it in the constructor. Can you do it upon item selection or `Button.Click` instead? – Sheridan Nov 04 '13 at 17:32
  • Yes, this works. I created a thread that invokes the main dispatcher...however, still kind of puzzled because even this code doesn't exactly get the right point I want the window to be at. It aligns it to the top left of the screen. :( – Alexandru Nov 04 '13 at 19:07
  • Were you using the `MainWindow` or another element as the parameter? The `GetPosition` method should get the position of the mouse relative to that specified element... if that element was not visible then I guess it might not work correctly. – Sheridan Nov 04 '13 at 19:15
  • I'm trying to use it on the Loaded event of the new window that I pop open. Not MainWindow. However, mousePositionInScreenCoordinates seems to be (0, 0). You're saying this will change even later on? I can try to thread it out. – Alexandru Nov 04 '13 at 22:52
  • Even if delayed the mousePositionInScreenCoordinates seems to always be (0, 0). – Alexandru Nov 04 '13 at 22:57
  • No, using it in the `Loaded` event handler should be fine. Unfortunately, I can't really test it out, so I'm not sure what's going wrong. – Sheridan Nov 05 '13 at 09:17
2

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();
Jan Gassen
  • 3,406
  • 2
  • 26
  • 44
0

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