WPF: drawing own cursor - nontrivial problem - c#

i need to implement a cursor with some very specific features:
it has to be animated
because after n seconds it automatically clicks - so the animation is feedback for the user when the click will happen
it has to snap to some of our controls
it has to work outside of our application
the approaches so far:
render my WPF-control into a bitmap, make a cursor-struct out of it and use user32.dll/SetSystemCursor to set it
PRO
the cursor has no delay after the mouse since it's a real cursor
CON
snapping is quite hard, especially since we have absolute and relative inputdevices and i would have to reset the mouseposition all the time or use user32.dll/ClipCursor (System.Windows.Forms.Cursor.Clip does the same) but the snapped cursor is always shaking around the snapped position (tries to escape, get's reset again....)
the code i use throws strange exceptions after some random time - so my current code seems quite unstable
render my own cursor into a maximized, topmost, allowtransparent, windowstyle=none, invisible window and manually move the cursor after the mouse (like Canvas.SetLeft(cursor, MousePosition.X))
PRO
snapping can be (easily) done
CON
when the mouse clicks and hit's the cursor the cursor get's clicked and not the window beyond
polling the mouseposition in a dispatcher-background-loop all the time doesn't seem very beautiful to me
to solve the second approach my cursor would have to have at least one transparent pixel
in the hotspot, so that the mouse can click through... that doesn't seem like a real solution to me...
any idea's anyone?
EDIT:
some example-source to show the problems...:
example app & source to show the problem with snapping the mouse to a fixed position: ClipIt.rar
example app & source that fails after random time - setting a self-drawn cursor: TryOwnCur.rar
can be found under: http://sourcemonk.com/Cursor

thanks to http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/a3cb7db6-5014-430f-a5c2-c9746b077d4f
i can click through my self-drawn cursor which follows the mouse-position by
setting the window style:none, and allowtransparent as i already did and
then
public const int WS_EX_TRANSPARENT = 0x00000020;
public const int GWL_EXSTYLE = (-20);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hwnd,
int index);
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hwnd,
int index, int newStyle);
public static void makeTransparent(IntPtr hwnd) {
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
}
and call makeTransparent from OnSourceInitialized...

Related

Setting cursor position in WPF only works sometimes

I am working on a WPF application and want to set the cursor position to the corner of the screen.
I tried using the WinForms approach:
System.Windows.Forms.Cursor.Position = new System.Drawing.Point(0, 0);
and the user32.dll approach:
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool SetCursorPos(int X, int Y);
The WinForms approach worked sometimes, but I don't understand why it worked and didn't work sometimes. I made sure the code is actually being called. The user32.dll approach didn't work for me at all.
Simple solution is make your application enough big and move mouse inside of it.
http://www.aspdotnet-pools.com/2017/10/move-mouse-cursor-automatically-c.html
To move mouse outside your application you need global mouse hook.
Global mouse event handler

Restrict Mouse Move only on a Specified Area

private void MoveCursor()
{
// Set the Current cursor, move the cursor's Position,
// and set its clipping rectangle to the form.
this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(Cursor.Position.X - 50, Cursor.Position.Y - 50);
Cursor.Clip = new Rectangle(this.Location, this.Size);
}
I am using the above code to Restrict the movement but still I am able to move the mouse outside the form?
Can I restrict the mouse movement to a specified area in the form? Please advise...
Updated answer:
ClipCursor is the API function you require. You will need to supply screen-based coordinates.
BOOL WINAPI ClipCursor(RECT *lpRect);
take a look at this link for the Win32 API code, and this one for pinvoke from C#.
There is pair of Win32 API functions called SetCapture/ReleaseCapture that will restrict the mouse to a certain window bounds.
You will need to use PInvoke to use them, but this will work.
[DllImport("user32.dll")]
static extern IntPtr SetCapture(long hWnd);
SetCapture(Control.Handle);
One thing to bear in mind, is that if used incorrectly, it's possible that the user will not be able to click the [X] to shut down your application because the mouse will not be able to get to the title bar.

alternate moving /hightlighting a control using x/y postioning

I'm looking for an api that can cause a windows select/highlight event to occur on a windows desktop, without actually causing the mouse cursor to move.. I can cause the mouse cursor to move with :
public static extern bool SetCursorPos(int X, int Y);
But that moves the actual cursor to that point... I'm looking for a way to highlight only as one might do by using the tab and arrow keys to move around the windows desktop. Any suggestions are appreciated..
regards, rob
I think you may be looking for SetFocus. You can get a control's handle with Control.Handle or FindWindow , and p/invoke SetFocus (use IntPtr as the argument type).

how do you simulate a mouse click by pressing a keyboard button in C#

I have been trying to find a way to make a mouse click programmatically but the results I find are quit strainous based on my level. I know how to position the mouse and everything but the click. I also know that there is a way to simulate a keyboard press with key events. So this got me to wonder, is there a way to make the mouse click by pressing a keyboard key?
I want to do this because I'm working on a educational project that shows beginners how to do simple functions on the computer, like how to create a file or open certain programs, so I need the mouse click to work based on the screen and outside of my app. Is this possible? all help will be appreciated.
you can simulate Mouse Click though following code
using System.Runtime.InteropServices;
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
public void DoMouseClick()
{
//Call the imported function with the cursor's current position
int X = Cursor.Position.X;
int Y = Cursor.Position.Y;
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
}
mouse_event actually perform the moouse click.
This is not a c# answer but if you are trying to only automate stuff for teaching, this might help. You can capture a video of all your actions and highlight mouse clicks. A good software for that is the Camtasia studio. It is a paid software but there are other free screen recording software out there.
If you really want to just automate actual mouse clicks, try using Autoit
http://www.autoitscript.com/site/autoit/
for animations. You can script anything from keyboard keypresses to mouse clicks in any area of the screen.
In case you want to try it out, to click on any position, all you would do is
MouseClick("primary", x-coordinate, y-coordinate, number-of-clicks)
// number of clicks = 2 for double click
$pos = MouseGetPos()
$pos[0] // contains the x coordinate of current mouse pos
$pos[1] // contains the y coord of current position

Clickthrough to desktop on a transparent form

I have a screen capturing utility on which I can rubber band an area on the desktop. I've done this is a fairly easy manner, I have a form which is the same size as the screen on which I draw a screenshot of the desktop transformed into grayscale. When the user holds down the left mouse button he/she can select an area on the form. The rectangle which the user draws is filled with TransparentColor. Once the users lifts up his/her mouse the transparent rectangle is left in place and the actual desktop is visible. Here comes my problem:
On my dev PC I can actually click through this transparent rectangle and navigate around etc. while on my other PC the form responds on mouse clicks on the actual transparent rectangle.
I'm using .NET 4.0 in C#, any ideas on how I can make it actually click through to the desktop on all cases??
Thank you and much appreciated :)
I managed to find a proper solution for this problem after looking very deeply into this. It turns out with the proper Win32 API calls it is possible to set a form "Invisible" to mouse clicks. This can be achieved by:
public const int GWL_EXSTYLE = -20;
public const uint WS_EX_LAYERED = 0x00080000;
public const uint WS_EX_TRANSPARENT = 0x00000020;
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
public void SetFormTransparent(IntPtr Handle) {
oldWindowLong = GetWindowLong(Handle, GWL_EXSTYLE);
SetWindowLong(Handle, GWL_EXSTYLE, Convert.ToInt32(oldWindowLong | WS_EX_LAYERED | WS_EX_TRANSPARENT));
}
public void SetFormNormal(IntPtr Handle) {
SetWindowLong(Handle, GWL_EXSTYLE, Convert.ToInt32(oldWindowLong | WS_EX_LAYERED));
}
But there is a trick to everything. You need to be careful that all clicks made on the forum will fall through the form and be sent to anything below the form. To ensure that if I click on my form e.g. on a button and I want the button clicked I did a simple trick. I have a timer in the background running every N milliseconds and analyzing the position of the Cursor. If it's above the area I want it to be, it'll set the Form to Normal via SetFormNormal() otherwise it'll be transparent.
Hope this code bit helps and people will use it.

Categories

Resources