C# Screen size without references in interactive - c#

I want to get the screen size of the primary screen, without adding any references (e.g. WinForms or Presentation). I found a similar question here, however there is no solution which doesn't include downloading or something like that.
But I want to make a method, which can be executed in the C# interactive on any other pc. Therefore I need a solution that doesn't reference other stuff than the standard (E.g. System, System.Core, ... is allowed).
I do know this is possible with
System.Windows.Forms.Screen.PrimaryScreen.Bounds;
but as this requires the System.Windows.Forms reference, it's not suitable for me. But basically the result of this snippet is what I want to get without references.

Here's an example I came up with.
I have noticed that it does not work correctly on High-DPI Screens. It will report the apparent resolution, not the actual resolution.
static void Main(string[] args)
{
var size = GetScreenSize();
Console.WriteLine(size.Length + " x " + size.Width);
Console.ReadLine();
}
static Size GetScreenSize()
{
return new Size(GetSystemMetrics(0), GetSystemMetrics(1));
}
struct Size
{
public Size(int l, int w)
{
Length = l;
Width = w;
}
public int Length { get; set; }
public int Width { get; set; }
}
[DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern int GetSystemMetrics(int nIndex);

If you don't want to use those libraries, You'll probably need to use native methods. I can't think of a way around this, as you'll need to communicate with the system any way you go.
A good place to start would be the source of System.Windows.Forms.Screen
Here's a link to the source. I bet you could strip down their code, and get the bare minimum.

I actually found a solution to this:
using System;
using System.Runtime.InteropServices;
static void Main()
{
EnumWindows(E, IntPtr.Zero);
Console.Write($"{_.Item1}x{_.Item2}");
}
struct R
{
int l;
int t;
public int r;
public int b;
public override string ToString() => $"{l},{t},{r},{b}";
public bool i() => l == 0 && r != 00;
}
static (int, int) _;
static bool E(IntPtr w, IntPtr l)
{
var r = new R();
GetWindowRect(w, ref r);
if (r.i() && _.Item1 == 0)
_ = (r.r, r.b);
return true;
}
delegate bool P(IntPtr w, IntPtr l);
[DllImport("user32.dll")]
static extern bool EnumWindows(P e, IntPtr l);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr w, ref R r);
Main()
Paste this into your interactive and it should output the screen resolution - at least it does for me.
Don't ask me how it works, it's stumped together from different tutorials and pressed into the interactive. Therefore I can't guarantee this will work on every pc.
Could somebody else please test this?

Related

How to get position of c# console application

I am a beginner at coding and I want to create an application that says the width, height, and position of the window.
The problem is that I don't know how to GET the position of the window.
I searched the internet but couldn't find the answer to my question.
Here is the code I have:
using System;
namespace WindowSizeChecker
{
class Program
{
const bool alwaysTrue = true;
static void Main(string[] args)
{
while (alwaysTrue == true)
{
Console.Write("Set your console window to your prefered size and position. Then press Enter");
Console.ReadLine();
screenSizeAndPosition();
Console.WriteLine("\n\nPress enter to repeat\n\n");
Console.ReadLine();
}
}
public static void screenSizeAndPosition()
{
int consoleWidth = Console.WindowWidth;
int consoleHeight = Console.WindowHeight;
string consoleWidthString = consoleWidth.ToString();
string consoleHeightString = consoleHeight.ToString();
Console.WriteLine("\nThe width of the window is: {0}\nAnd the height of the window is: {1}", consoleWidthString, consoleHeightString);
int largestWindowWidth = Console.LargestWindowWidth;
int largestWindowHeight = Console.LargestWindowHeight;
string largestWindowWidthString = largestWindowWidth.ToString();
string largestWindowHeightString = largestWindowHeight.ToString();
Console.WriteLine("\nThe largest width of the window is: {0}\nAnd the largest height of the window is: {1}", largestWindowWidthString, largestWindowHeightString);
}
}
}
Here is the program running:
enter image description here
I am a beginner at coding and I want to create an application that says the width, height, and position of the window. The problem is that I don't know how to GET the position of the window. I searched the internet but couldn't find the answer to my question.
The information is out there, but I admit, it's not necessarily presented in the easiest to understand manner, especially for a beginner.
IMHO, two of the most relevant Stack Overflow questions you probably should read are these:
Position a small console window to the bottom left of the screen?
DwmGetWindowAttribute returns 0 with PInvoke
They aren't really duplicates of your question, and for a beginner it's probably hard to see how they answer it. But they do in fact contain almost all of the information you would need.
There are a couple of things you need to understand, besides the "how":
The Console properties you're looking at now are not pixel dimensions, but rather are in terms of character columns and rows. That is, how many characters can fit across the window in a single row, and how many rows of those characters can fit vertically.
When it comes to pixels, there are actually (at least) two different ways to look at the window size: raw screen coordinates, and "DPI-adjusted". The latter is IMHO a misnomer, because it's not taking into account any actual screen resolution (i.e. "dots per inch"), but rather the scaling factor that is set for your desktop. It's considered "DPI-adjusted" because setting the scaling factor is the Windows mechanism for attempting to keep the visual presentation of a program consistent across displays of different resolution.
As you already have seen, you can get the character-oriented dimensions straight from the .NET Console class. But to get the pixel information, you need to use .NET's native interop support to call the Windows API directly. Here are some helper classes I put together, based on available documentation and Stack Overflow posts, to do that for your scenario:
[StructLayout(LayoutKind.Sequential)]
struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public int Width => Right - Left;
public int Height => Bottom - Top;
}
class NativeConsole
{
[DllImport("kernel32")]
public static extern IntPtr GetConsoleWindow();
}
class Winuser
{
[DllImport(#"user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, out Rect lpRect);
public static Rect GetWindowRect(IntPtr handle)
{
if (!GetWindowRect(handle, out Rect rect))
{
throw Marshal.GetExceptionForHR(Marshal.GetLastWin32Error());
}
return rect;
}
}
class DwmApi
{
private const int DWMWA_EXTENDED_FRAME_BOUNDS = 9;
[DllImport(#"dwmapi.dll")]
private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute);
public static Rect GetExtendedFrameBounds(IntPtr hwnd)
{
int hresult = DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, out Rect rect, Marshal.SizeOf(typeof(Rect)));
if (hresult != 0)
{
throw Marshal.GetExceptionForHR(hresult);
}
return rect;
}
}
You could, of course, lump all of the above together in a single class, but I prefer to keep things organized. The above groups the various parts of the API into the same organization used in the native Win32 API itself.
With those pieces in hand, now we can put together a program similar to the one you have above, except that it will display the window position (which is what you want), along with the width and height as well (since those come for free from the native API anyway).
That looks like this:
using static System.Console;
class Program
{
static void Main(string[] args)
{
Clear();
string prompt = "Set your console window to your preferred size and position. Press X to exit";
while (true)
{
if (KeyAvailable && ReadKey(intercept: true).Key == ConsoleKey.X)
{
break;
}
ScreenSizeAndPosition(prompt);
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(0.25));
}
}
static void ScreenSizeAndPosition(string prompt)
{
string format = $"{{0, {-WindowWidth}}}";
SetCursorPosition(0, 0);
Write(format, prompt);
Write(format, $"Window is {WindowWidth} columns wide and {WindowHeight} rows high");
Write(format, $"The largest window that can fit on the screen is {LargestWindowWidth} columns wide and {LargestWindowHeight} rows high");
IntPtr consoleHwnd = NativeConsole.GetConsoleWindow();
Rect winuserRect = Winuser.GetWindowRect(consoleHwnd),
dwmRect = DwmApi.GetExtendedFrameBounds(consoleHwnd);
Write(format, $"DPI-adjusted screen values: location is {{{winuserRect.Left}, {winuserRect.Top}}}, window is {winuserRect.Width} pixels wide, {winuserRect.Height} pixels high");
Write(format, $"Desktop Window Manager values: location is {{{dwmRect.Left}, {dwmRect.Top}}}, window is {dwmRect.Width} pixels wide, {dwmRect.Height} pixels high");
for (int i = 0; i < WindowHeight - 5; i++)
{
Write(format, "");
}
}
}
I did change your basic logic in the program a bit, so that it just checks every quarter second rather than waiting for the user to press a key, displaying whatever the current values are as the user changes the window size and position.
For more details on the Windows functions used above, you should read the documentation:
GetConsoleWindow()
GetWindowRect()
DwmGetWindowAttribute()
There are some additional subtleties in the ways that GetWindowRect() and DwmGetWindowAttribute() work, so it's worth checking out the docs so that you understand better what they are returning in terms of the window dimensional values.
You should call a Win32 API DwmGetWindowAttribute via PInovke to the current window position. Refer to the document and see how to use it.

Determine if Windows 10 Touch Keyboard is Visible or Hidden

I am trying to find out if the windows 10 virtual touch keyboard is visible or not to know whether to open it or not from my application. THe following code has worked fine up until the latest Windows 10 update 15063 or possible the one right before it. Seems like Microsoft changed something with the window styles possibly but I can't figure it out.
public static bool IsKeyboardVisible()
{
IntPtr keyboardHandle = GetKeyboardWindowHandle();
// Specifies we wish to retrieve window styles.
int GWL_STYLE = -16;
//The window is disabled. See http://msdn.microsoft.com/en-gb/library/windows/desktop/ms632600(v=vs.85).aspx.
UInt32 WS_VISIBLE = 0x10000000;
UInt32 WS_DISABLED = 0x08000000;
UInt32 WS_POPUP = 0x80000000;
bool visible = false;
bool disabled = false;
if (keyboardHandle != IntPtr.Zero)
{
UInt32 style = GetWindowLong(keyboardHandle, GWL_STYLE);
visible = ((style & WS_VISIBLE) == WS_VISIBLE);
disabled = ((style & WS_DISABLED) == WS_DISABLED); // ref https://stackoverflow.com/questions/11065026/get-window-state-of-another-process
log.InfoFormat("style:{0:X4} visible:{1} disabled:{2}", style, visible, disabled);
}
return visible && !disabled ;
}
This is related to: Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition
I've done some research with Spy++ . Looks like the new keyboard in Fall Creators Update (ver. 1709) is hosted by another window. This window has Windows.UI.Core.CoreWindow class and Microsoft Text Input Application as its title.
The following code works for all Windows 10 versions including the new 1803 and older Windows versions as well (starting with Windows 8, I believe).
static class TouchKeyboard
{
public static bool GetIsOpen()
{
return GetIsOpen1709() ?? GetIsOpenLegacy();
}
private static bool? GetIsOpen1709()
{
var parent = IntPtr.Zero;
for (;;)
{
parent = FindWindowEx(IntPtr.Zero, parent, WindowParentClass1709);
if (parent == IntPtr.Zero)
return null; // no more windows, keyboard state is unknown
// if it's a child of a WindowParentClass1709 window - the keyboard is open
var wnd = FindWindowEx(parent, IntPtr.Zero, WindowClass1709, WindowCaption1709);
if (wnd != IntPtr.Zero)
return true;
}
}
private static bool GetIsOpenLegacy()
{
var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass);
if (wnd == IntPtr.Zero)
return false;
var style = GetWindowStyle(wnd);
return style.HasFlag(WindowStyle.Visible)
&& !style.HasFlag(WindowStyle.Disabled);
}
private const string WindowClass = "IPTip_Main_Window";
private const string WindowParentClass1709 = "ApplicationFrameWindow";
private const string WindowClass1709 = "Windows.UI.Core.CoreWindow";
private const string WindowCaption1709 = "Microsoft Text Input Application";
private enum WindowStyle : uint
{
Disabled = 0x08000000,
Visible = 0x10000000,
}
private static WindowStyle GetWindowStyle(IntPtr wnd)
{
return (WindowStyle)GetWindowLong(wnd, -16);
}
[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string className, string title = null);
[DllImport("user32.dll", SetLastError = false)]
private static extern uint GetWindowLong(IntPtr wnd, int index);
}
Update: I updated the answer and the code to be compatible with Redstone 4 (v1803) as well.
I'm using this solution, and it is working on Windows 1607, 1709 and 1803 (check the Main method below on the code):
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
[ComImport, Guid("D5120AA3-46BA-44C5-822D-CA8092C1FC72")]
public class FrameworkInputPane
{
}
[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("5752238B-24F0-495A-82F1-2FD593056796")]
public interface IFrameworkInputPane
{
[PreserveSig]
int Advise(
[MarshalAs(UnmanagedType.IUnknown)] object pWindow,
[MarshalAs(UnmanagedType.IUnknown)] object pHandler,
out int pdwCookie
);
[PreserveSig]
int AdviseWithHWND(
IntPtr hwnd,
[MarshalAs(UnmanagedType.IUnknown)] object pHandler,
out int pdwCookie
);
[PreserveSig]
int Unadvise(
int pdwCookie
);
[PreserveSig]
int Location(
out Rectangle prcInputPaneScreenLocation
);
}
static void Main(string[] args)
{
var inputPane = (IFrameworkInputPane)new FrameworkInputPane();
inputPane.Location(out var rect);
Console.WriteLine((rect.Width == 0 && rect.Height == 0) ? "Keyboard not visible" : "Keyboard visible");
}
}
}
It uses the IFrameworkInputPane interface (https://learn.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-iframeworkinputpane)
I discovered yet another undocumented COM API that returns the position of the touch keyboard. It returns the bounds of the keyboard window or zeroes if the keyboard is hidden. I tested it in Windows 8.1, Windows 10 and Windows 10 Fall Creators Update and it works fine.
Now some bad news: in all versions prior to Fall Creators Update it only reports accurate results if the active window and the touch keyboard are located on the same monitor. If this is not the case - the API just returns the previous cached value. I'm guessing it has something to do with the fact that this API was meant to be used to calculate occlusion of the touch keyboard and your app's window. (It's called inside Windows::UI::ViewManagement::InputPane.get_OccludedRect() UWP API).
So if you don't care about supporting older versions or multi-monitor scenarios - use it. Otherwise I would suggest checking the Windows version and falling back to the previous method (GetIsOpenLegacy() from my other answer).
The API:
[ComImport, Guid("228826af-02e1-4226-a9e0-99a855e455a6")]
class ImmersiveShellBroker
{
}
[ComImport, Guid("9767060c-9476-42e2-8f7b-2f10fd13765c")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IImmersiveShellBroker
{
void Dummy();
IInputHostManagerBroker GetInputHostManagerBroker();
}
[ComImport, Guid("2166ee67-71df-4476-8394-0ced2ed05274")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IInputHostManagerBroker
{
void GetIhmLocation(out Rect rect, out DisplayMode mode);
}
[StructLayout(LayoutKind.Sequential)]
struct Rect
{
public int Left, Top, Right, Bottom;
}
enum DisplayMode
{
NotSupported = 0,
Floating = 2,
Docked = 3,
}
Usage example:
// do this once:
var brokerClass = new ImmersiveShellBroker();
var broker = (IImmersiveShellBroker)brokerClass;
var ihm = broker.GetInputHostManagerBroker();
Marshal.ReleaseComObject(broker);
// now ihm reference can be cached and used later:
Rect rect;
DisplayMode mode;
ihm.GetIhmLocation(out rect, out mode);
Note: looks like GetIhmLocation() always returns DisplayMode.NotSupported instead of the actual mode prior to Windows 10.

Virtual ListView with artifacts in short list for Windows 8 and higher

I have a ListView in a Winform User Control. The VirtualMode is true and the VirtualListSize is 200. When there are less items in the ListView than visible rows, I get weird characters below the (real) items. These "artifacts" appear when the software is run on Windows 8, 10 or Windows Server 2012, but not on Windows 7.
Does anyone know what could be causing these "artifacts"? I added a character "A" "B", etc. to the Title of all the places where ListViewItems are created. So I know that none of the code in this user control is creating them. I added a sample solution that shows the problem below.
Sometimes they appear as chinese characters, sometimes just a random letter and character combination. Usually they are not longer than 4 characters.
[Update] It does not occur on the latest Version of Windows 10.
[Update2] I was able to reproduce the problem on a small sample solution. Find the zip file here.
I ended up assigning the number of visible lines of the ListView to the VirtualListSize property, whenever the number of items in the ListView changed.
I used the following class to determine the number of visible lines in the ListView:
public static class ListViewSizer
{
const UInt32 LVM_FIRST = 0x1000;
const UInt32 LVM_GETHEADER = (LVM_FIRST + 31);
private static int GetHeaderHeight(ListView listView)
{
Rect rect = new Rect();
IntPtr hwnd = SendMessage((IntPtr)listView.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
if (hwnd != null)
{
if (GetWindowRect(new System.Runtime.InteropServices.HandleRef(null, hwnd), out rect))
{
return rect.Bottom - rect.Top;
}
}
return -1;
}
public static int GetLastVisibleLine(ListView listView)
{
int firstVisibleIndex = listView.TopItem.Index;
int heightOfFirstItem = listView.GetItemRect(firstVisibleIndex, ItemBoundsPortion.Entire).Height;
// assuming each item has the same height (I think this is true for list view and details view)
const int DefaultVisibleLines = 11;
int visibleLines = heightOfFirstItem != 0 ? (int)Math.Ceiling((decimal)((listView.Height - GetHeaderHeight(listView)) / heightOfFirstItem)) : DefaultVisibleLines;
int lastVisibleIndexInDetailsMode = firstVisibleIndex + visibleLines;
return lastVisibleIndexInDetailsMode;
}
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool GetWindowRect(System.Runtime.InteropServices.HandleRef hwnd, out Rect lpRect);
[Serializable, System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}
I know this is a bit of a hack. The artifacts would still be there, but you just can't see them anymore, because the ListView always has as many lines as can be displayed. This is the best I could come up with without changing the control.

How to write an localized on-screen-keyboard

I have to write an on screen keyboard for our company's program, which is mostly used on industry's PCs with touch capability.
We can't use the windows default keyboard because we don't need all keys on the keyboard. So I was asked to write a custom one in C#.
I already found this blog as reference, but I'm not sure how to start.
I created a small prototype GUI and assign for each key a scancode, and translate these scancodes to the related character. And send them to the active control. But I'm not sure what scancodes I should use.
So my question is, is that the correct way to write a OSK like this and if yes which scancodes should I use? Any links?
I'm also not sure how to handle the shift states...
Edit:
Okay I did a bit more research and came up with a osk which reads the current keyboard layout and even handles the easy shift states (Shift and Alt Gr). I wrote a KeyButton class which inherits from Button, this KeyButton has a ScanCode property of type byte and if you assign a valid scancode to it, the KeyButton will call the related functions to get the correct text. I used the functions from Michael Kaplan blogs with some small changes. In the end it turned out that I just had to do the same as he did.
So the answer to my question is: Yes, you have to use scancodes on your buttons and then get the virtualkey and the unicode from the keyboard layout. Use these scancodes.
Now I get the characters the only thing left is to send these around.
I think this is fairly simple, just make a series of buttons and assign each button a letter, and inside the buttons Click method you can do a simple.
SendKeys.Send("A");
Changing key based on button etc
I wrote mapping classes that map key code to character for WPF application.
May be this can help.
public class KeyMapper
{
/// <summary>
/// Map key code to character.
/// If key code cannot be mapped returns empty char.
/// </summary>
public static char MapKey(Key key, bool shiftPressed, string culture)
{
CheckCulture(culture);
int englishVirtuaCode = KeyInterop.VirtualKeyFromKey(key);
return EnglishVirtualCodeToChar(englishVirtuaCode, shiftPressed, culture);
}
private static void CheckCulture(string culture)
{
InputLanguage language = InputLanguage.FromCulture(new CultureInfo(culture));
if (language == null)
throw new ArgumentException(string.Format("culture {0} does not exist.", culture));
}
private static char EnglishVirtualCodeToChar(int enlishVirtualCode, bool shiftPressed, string culture)
{
var scanCode = KeyMappingWinApi.MapVirtualKeyEx((uint)enlishVirtualCode, 0, EnglishCultureHandle);
var vitualKeyCode = KeyMappingWinApi.MapVirtualKeyEx(scanCode, 1, GetCultureHandle(culture));
byte[] keyStates = GetKeyStates(vitualKeyCode, shiftPressed);
const int keyInformationSize = 5;
var stringBuilder = new StringBuilder(keyInformationSize);
KeyMappingWinApi.ToUnicodeEx(vitualKeyCode, scanCode, keyStates, stringBuilder, stringBuilder.Capacity, 0, GetCultureHandle(culture));
if (stringBuilder.Length == 0)
return ' ';
return stringBuilder[0];
}
private static IntPtr EnglishCultureHandle
{
get { return GetCultureHandle("en-US"); }
}
private static IntPtr GetCultureHandle(string culture)
{
return InputLanguage.FromCulture(new CultureInfo(culture)).Handle;
}
/// <summary>
/// Gets key states for ToUnicodeEx function
/// </summary>
private static byte[] GetKeyStates(uint keyCode, bool shiftPressed)
{
const byte keyPressFlag = 0x80;
const byte shifPosition = 16; // position of Shift key in keys array
var keyStatses = new byte[256];
keyStatses[keyCode] = keyPressFlag;
keyStatses[shifPosition] = shiftPressed ? keyPressFlag : (byte)0;
return keyStatses;
}
}
public class KeyMappingWinApi
{
[DllImport("user32.dll")]
public static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, IntPtr dwhkl);
[DllImport("user32.dll")]
public static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState,
[Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
[DllImport("user32.dll")]
public static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
}
You might want to check out these guys:
http://cnt.lakefolks.com/
Might be cheaper just to buy it rather than develop it, ya never know ;-)

How to get mouse position related to desktop in WPF?

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.
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));
}
}
Two options:
Use System.Windows.Forms.Control.MousePosition, or p/invoke
[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
public static extern bool GetCursorPos([In, Out] NativeMethods.POINT pt);
The first option already does the p/invoke for you. I'm not entirely sure it requires you have some UI splashed up, but I don't think so. Yes, its winforms and not wpf, but it really doesn't have anything to do with where its located at.
If you want to skip any dependencies on system.windows.forms.dll then check out more information about the second on pinvoke.net.
I stumbled over that thread while looking for a solution for the same problem. In the meantime, I found PointToScreen, which does not require any P/Invoke. The method is available on any Visual starting .NET 3.0 (and thus UIElement, Control, etc.) and an implementation would look like this:
protected void OnMouseLeave(object Sender, MouseEventArgs e) {
var relativePosition = e.GetPosition(this);
var screenPosition = this.PointToScreen(relativePosition);
}

Categories