C# WPF - Get mouse coordinates outside of WPF Window - c#

I've made it so that when I hit CTRL+Capslock a WPF window shows. How would I retrieve mouse coordinates to be able to make the window pop up right next to the mouse no matter where my mouse is currently at.
So I hit CTRL+Capslock it retrieves cursor coordinates and positions window next to cursor and shows it at the same time.
I have been unable to find anything that actually works. - Thank you guys in advance. :)
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_HOTKEY = 0x0312;
switch (msg)
{
case WM_HOTKEY:
switch (wParam.ToInt32())
{
case HOTKEY_ID:
int vkey = (((int)lParam >> 16) & 0xFFFF);
if (vkey == VK_CAPITAL)
{
if (Application.Current.MainWindow.WindowState == WindowState.Minimized)
{
//Application.Current.MainWindow.Left = point
//Application.Current.MainWindow.Top =
Application.Current.MainWindow.WindowState = WindowState.Normal;
}
else
{
Application.Current.MainWindow.WindowState = WindowState.Minimized;
}
}
handled = true;
break;
}
break;
}
return IntPtr.Zero;
}

If you can use P/Invoke, you can use GetCursorPos:
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
Usage:
Point cursorCoords = new();
GetCursorPos(ref cursorCoords);
// cursorCoords now holds the screen coordinates of the cursor
...with Point being a sequentially laid out structure that contains an X and a Y coordinate. You can find such a structure in the System.Drawing namespace.

Related

Disable window reaction to ShowWindow function call

I have a WPF window with disabled resize capability (WindowStyle=None, ResizeMode = CanMinimize).
It works fine, except one situation. If some application (i.e AutoHotKey) calls a WIN API function ShowWindow on my window with SW_MAXIMIZE option, then my window repositions to (0, 0) coordinate on the desktop with no size change and user is not able to move it over the screen further.
How can I disable this behavior? I want the window to ignore this call on my window.
I have tried reacting to WM_WINDOWPOSCHANGING event, but this works only until user minimizes the window. After it is minimized and restored it becomes repositioned to (0, 0) coordinate again.
private IntPtr ProcessMessage(IntPtr windowHandle, int msg, IntPtr wideParam, IntPtr leftParam, ref bool handled)
{
Msg windowsMessage = (Msg)msg;
switch (windowsMessage)
{
case Msg.WM_WINDOWPOSCHANGING:
{
WindowPos windowPos = (WindowPos)Marshal.PtrToStructure(leftParam, typeof(WindowPos));
if (IsNoClientAction(windowPos) && !IsMinimizing(windowPos) && window.WindowState == WindowState.Normal)
{
windowPos.Flags = SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOMOVE;
handled = true;
Marshal.StructureToPtr(windowPos, leftParam, true);
}
break;
}
}
return IntPtr.Zero;
}
private static bool IsNoClientAction(WindowPos windowPos)
{
return (windowPos.Flags & SetWindowPosFlags.SWP_NOCLIENTSIZE) != 0 || (windowPos.Flags & SetWindowPosFlags.SWP_NOCLIENTMOVE) != 0;
}
private static bool IsMinimizing(WindowPos windowPos)
{
return windowPos.Left == -32000 && windowPos.Top == -32000;
}
I have analyzed what user32.dll does to the window and figured out solution based on that. Before sending any messages to Wndproc it updates GWL_STYLE flags of the window enabling WS_MAXIMIZE for it. For this reason window state becomes corrupted and further behavior can hardly be handled by processing window messages only.
To disable window reaction on ShowWindow with SW_MAXIMIZE option I am setting this flag back when WM_WINDOWPOSCHANGING is processed:
private IntPtr ProcessMessage(IntPtr windowHandle, int msg, IntPtr wideParam, IntPtr leftParam, ref bool handled)
{
Msg windowsMessage = (Msg)msg;
switch (windowsMessage)
{
case Msg.WM_WINDOWPOSCHANGING:
{
WindowPos windowPos = (WindowPos)Marshal.PtrToStructure(leftParam, typeof(WindowPos));
if (IsNoClientAction(windowPos))
{
WindowStyles styles = (WindowStyles)WindowsAPI.GetWindowLongPtr(windowHandle, GWL.GWL_STYLE);
if ((styles & WindowStyles.WS_MAXIMIZE) != 0)
{
windowPos.Flags |= SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOMOVE;
WindowsAPI.SetWindowLongPtr(new HandleRef(this, windowHandle), GWL.GWL_STYLE, (IntPtr)(long)(styles ^ WindowStyles.WS_MAXIMIZE));
handled = true;
Marshal.StructureToPtr(windowPos, leftParam, true);
}
}
break;
}
}
return IntPtr.Zero;
}
private static bool IsNoClientAction(WindowPos windowPos)
{
return (windowPos.Flags & SetWindowPosFlags.SWP_NOCLIENTSIZE) != 0 || (windowPos.Flags & SetWindowPosFlags.SWP_NOCLIENTMOVE) != 0;
}

Is there a way to auto scroll a listbox in C# WinForms

I an writing a program will display a list of number in serveral list boxes on a big monitor, my question is is there a way to auto scroll the list box to show all data in the boxes?
Normally, I would do:
listBox.SelectedIndex = listBox.Items.Count - 1;
listBox.SelectedIndex = -1;
but you can also try
int nItems = (int)(listBox.Height / listBox.ItemHeight);
listBox.TopIndex = listBox.Items.Count - nItems;
Hope this helps :)
To directly control the scrolling without selecting items, you need to use the Win32 SetScrollPos method from User32.dll. Here is a extended class which gives you basic support:
public class ScrollableListView : ListView
{
private const int WM_VSCROLL = 0x115;
private enum ScrollBar : int { Horizontal = 0x0, Vertical = 0x1 }
public void SetScroll(int x, int y)
{
this.SetScroll(ScrollBar.Horizontal, x);
this.SetScroll(ScrollBar.Vertical, y);
}
public void SetScrollX(int position)
{
this.SetScroll(ScrollBar.Horizontal, position);
}
public void SetScrollY(int position)
{
this.SetScroll(ScrollBar.Vertical, position);
}
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
private void SetScroll(ScrollBar bar, int position)
{
if (!this.IsDisposed)
{
ScrollableListView.SetScrollPos((IntPtr)this.Handle, (int)bar, position, true);
ScrollableListView.PostMessage((IntPtr)this.Handle, ScrollableListView.WM_VSCROLL, 4 + 0x10000 * position, 0);
}
}
}
You can then set the X or Y scroll quickly and easily. This should work for other controls too.
If you want to make the control automatically scroll down and up, you would need to set up a recurring timer, with an interval of around 20 milliseconds. Keep track of the scroll position and direction, and increment or decrement it accordingly, sending the position to the control using these methods.
UPDATE:
I had a few problems with the SetScrollPos method posted above, primarily around the scrollbar moving, but the contents not. It's probably just a minor oversight, but in the meantime, here is a somewhat "outside the box" MarqueeListView solution..
First up, the enum representing which scrollbar to use. I used display names instead of the Win32 names for these (SB_HORIZ and SB_VERT) just to make things a little clearer.
public enum ScrollBarDirection : int { Horizontal = 0x0, Vertical = 0x1 }
Another enum for the scroll command codes themselves - I've trimmed out everything apart from Up (SB_LINEUP), Down (SB_LINEDOWN) and EndScroll (SB_ENDSCROLL). EndScroll is needed after the scroll message to inform the control to update.
public enum ScrollCommand : int { Up = 0x0, Down = 0x1, EndScroll = 0x8 }
Then finally the class itself. It basically starts by scrolling down, every 20ms (by default - note this is changeable via the MarqueeSpeed property). It then gets the scroll position, and compares it to last time. Once the scroll bar stops moving, it reverses the direction. This is to get around problems I was having with the GetScrollInfo method.
public class MarqueeListView : ListView
{
protected const int WM_VSCROLL = 0x115;
private ScrollCommand scrollCommand;
private int scrollPositionOld;
private Timer timer;
public MarqueeListView()
: base()
{
this.MarqueeSpeed = 20;
this.scrollPositionOld = int.MinValue;
this.scrollCommand = ScrollCommand.Down;
this.timer = new Timer() { Interval = this.MarqueeSpeed };
this.timer.Tick += (sender, e) =>
{
int scrollPosition = MarqueeListView.GetScrollPos((IntPtr)this.Handle, (int)ScrollBarDirection.Vertical);
if (scrollPosition == this.scrollPositionOld)
{
if (this.scrollCommand == ScrollCommand.Down)
{
this.scrollCommand = ScrollCommand.Up;
}
else
{
this.scrollCommand = ScrollCommand.Down;
}
}
this.scrollPositionOld = scrollPosition;
MarqueeListView.SendMessage((IntPtr)this.Handle, MarqueeListView.WM_VSCROLL, (IntPtr)this.scrollCommand, IntPtr.Zero);
MarqueeListView.SendMessage((IntPtr)this.Handle, MarqueeListView.WM_VSCROLL, (IntPtr)ScrollCommand.EndScroll, IntPtr.Zero);
};
this.timer.Start();
}
public int MarqueeSpeed
{
get
{
return this.timer.Interval;
}
set
{
this.timer.Interval = value;
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
protected static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
}
Finally, here's a quick Main method to test it:
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form form = new Form() { StartPosition = FormStartPosition.CenterScreen, Width = 1280, Height = 720 };
MarqueeListView list = new MarqueeListView() { View = View.Tile, Dock = DockStyle.Fill };
for (int i = 0; i < 1000; i++) { list.Items.Add(Guid.NewGuid().ToString()); }
form.Controls.Add(list);
Application.Run(form);
}
Please bear in mind, this isn't necessarily the "proper" or best way of doing things, but I figured a different approach might give you some ideas!
I was hoping to use SetScrollPos which would give a much better, smoother effect. You can then easily include acceleration and deceleration - possibly optionally slowing to a halt on mouse over, then accelerating away on mouse out etc. It's just not playing ball at present though - I've got a working scroll update method in an old project somewhere, so I'll update this if I get it working again.
Hope that helps!
Even if it was a long time ago, others will certainly have the same problem. Since selecting the last item did not lead to automatic scrolling for me, here is a solution that works for me:
ListBox1.ScrollIntoView(ListBox1.Items[ListBox1.Items.Count - 1]);
Or just insert your items at the top by using:
lbLog.Items.Insert(0,"LogItem");

simulating mouse right click in my WinForm app

am trying to simulate mouse right click in my WinForm app as follow :
public const int WM_RBUTTONDOWN = 0x204;
public const int WM_RBUTTONUP = 0x205;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
public static IntPtr MakeLParam(int LoWord, int HiWord)
{
return (IntPtr)((HiWord << 16) | (LoWord & 0xffff));
}
public static void Click(int x, int y)
{
SendMessage(this.Handle, WM_RBUTTONDOWN, IntPtr.Zero, MakeLParam(x,y));
SendMessage(this.Handle, WM_RBUTTONUP, IntPtr.Zero, MakeLParam(x,y));
}
(x,y) the coords inside the app window form, but nothing happens what am missing here?
EDIT: I have also tried FindWindow(null,"Form1"); and it gives same Handle as this.Handle..
Why you dont use the Events built-in in Windows Forms??
Use de MouseDown Event of any control, example the Form:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
}
}
The MouseDown Event take the control in a any mouse button pressed.

Can't send Browser_Back/Forward via SendInput() (only on Surface Pro 2)

some days ago, I got my Microsoft Arc Touch Mouse Surface Edition. Unfortunately, it doesn't have Forward/Back-Buttons (it performs only Page-Up/Page-Down).
So, I've written a little program, that catches the press of the Page_Up/Page_Down Keys and sends instead the Browser-Back/-Forward Button.
On my PC (Win 8.1 Pro) this works quite well. However, on my Surface Pro 2 (for which the program primarly was written) it won't send the Browser_Back-/Forward-Buttons.
For every other key the program seems to work (like Space, letters, numbers).
Here the code for sending Keycodes:
public static uint send(short keyCode) {
INPUT structure = new INPUT();
structure.type = (int)InputType.INPUT_KEYBOARD;
structure.ki.wVk = keyCode;
structure.ki.dwFlags = (int)KEYEVENTF.KEYDOWN;
structure.ki.dwExtraInfo = GetMessageExtraInfo();
INPUT input2 = new INPUT();
structure.type = (int)InputType.INPUT_KEYBOARD;
structure.ki.wVk = keyCode;
input2.mi.dwFlags = (int)KEYEVENTF.KEYUP;
input2.ki.dwExtraInfo = GetMessageExtraInfo();
INPUT[] pInputs = new INPUT[] { structure, input2 };
return SendInput(2, pInputs, Marshal.SizeOf(structure));
}
And here the KeyboardHook:
public static int KeyHookProc(int nCode, IntPtr wParam, IntPtr lParam) {
KeyHookStruct myKeyHookStruct =
(KeyHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyHookStruct));
if (nCode < 0) {
return CallNextHookEx(hHook, nCode, wParam, lParam);
} else {
if (wParam == (IntPtr)0x0100) {
int keyCode = myKeyHookStruct.vkCode;
if (keyCode == 33) {
Input.send(0xA7);
} else if (keyCode == 34) {
Input.send(0xA6);
} else {
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
}
return 1;
}
}
So, if the Page-Down/page-Up button on my keyboard is pressed, the send()-Method is called.
0xA6 and 0xA7 are the Keycodes for Browser_Back and Browser_Forward.
On my Surface, the Page-Up/Page-Down buttons are recognized correctly. The send()-Method is also called, but it won'T send the correct Keycodes. According to Spy++, send() isn't sending anything if the Keycode is 0xA6 or 0xA7 (on my PC it works).
Where is the problem with the Surface Pro 2? What could I do, that it works, or how could I determine why it isn't working?
The code you posted is wrong, as you never set type for input2 (the code use structure.type)
EDIT:
From MSDN docs for SendInput:
This function fails when it is blocked by UIPI. Note that neither
GetLastError nor the return value will indicate the failure was caused
by UIPI blocking.
This function is subject to UIPI. Applications are permitted to inject
input only into applications that are at an equal or lesser integrity
level.
You also should remove the call to GetMessageExtraInfo
New Edit:
I don't like the logic of your KeyHookProc function: if the message is NOT WM_KEYDOWN (0x0100), then you return 1...
I would go for:
if (wParam != (IntPtr)0x0100) {
return CallNextHookEx(hHook, nCode, wParam, lParam);
} else {
int keyCode = myKeyHookStruct.vkCode;
if (keyCode == 33) {
Input.send(0xA7);
} else if (keyCode == 34) {
Input.send(0xA6);
} else {
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
return 1;
}

Non-resizeable, bordered WPF Windows with WindowStyle=None

Basically, I need a window to look like the following image: http://screenshots.thex9.net/2010-05-31_2132.png
(Is NOT resizeable, yet retains the glass border)
I've managed to get it working with Windows Forms, but I need to be using WPF. To get it working in Windows Forms, I used the following code:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x84 /* WM_NCHITTEST */)
{
m.Result = (IntPtr)1;
return;
}
base.WndProc(ref m);
}
This does exactly what I want it to, but I can't find a WPF-equivalent. The closest I've managed to get with WPF caused the Window to ignore any mouse input.
Any help would be hugely appreciated :)
A very simple solution is to set the Min and Max size of each window equal to each other and to a fix number in the window constructor. just like this:
public MainWindow()
{
InitializeComponent();
this.MinWidth = this.MaxWidth = 300;
this.MinHeight = this.MaxHeight = 300;
}
this way the user can not change the width and height of the window. also you must set the "WindowStyle=None" property in order the get the glass border.
You need to add a hook for the message loop :
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var interopHelper = new WindowInteropHelper(this);
var hwndSource = HwndSource.FromHwnd(interopHelper.Handle);
hwndSource.AddHook(WndProcHook);
}
private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x84 /* WM_NCHITTEST */)
{
handled = true;
return (IntPtr)1;
}
}

Categories

Resources