I have a form which has various buttons and panels. I have one button which when pressed runs a check against some values and if the check passes I need the mouse click to fall through the form and hit whatever it is beneath the application window.
What I'm currently doing is after the button is pressed and the check has passed, I set the form to transparent using:
[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);
private int oldWindowLong = 0;
public void SetFormTransparent(IntPtr Handle)
{
oldWindowLong = GetWindowLong(Handle, -20);
SetWindowLong(Handle, -20, Convert.ToInt32(oldWindowLong | 0x80000 | 0x20));
}
public void SetFormNormal(IntPtr Handle)
{
SetWindowLong(Handle, -20, Convert.ToInt32(oldWindowLong | 0x80000));
}
Then I create a 1 millisecond timer, I simulate the mouse click using:
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
And set the form back to normal. This results in a really inconsistent and sometimes slow/unresponsive behavior.
What other options do I have if I want to simulate a mouse click as soon as the button's check has passed?
The point is to use Color.Magenta as TransparencyKey and BackColor of your form.
Then make button invisible, and simulate a click event, then make the button visible again.
In this example, when you click on the button, it makes the form transparent and then simulates a click to pass through the form.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
public void PerformClick()
{
uint X = (uint)Cursor.Position.X;
uint Y = (uint)Cursor.Position.Y;
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
}
private void button1_Click(object sender, EventArgs e)
{
//Just to keep the form on top
this.TopMost = true;
//Make form transparent and click through
this.TransparencyKey = Color.Magenta;
this.BackColor = Color.Magenta;
//Make the button invisible and perform a click
//The click reaches behind the button
//Then make button visible again to be able handle clicks again
this.button4.Visible = false;
PerformClick();
this.button4.Visible = true;
}
Notes
Make Transparent and Click Through
To make a form Transparent and make clicks pass through the form, you can simply Set the TransparencyKey property and BackColor property of your form to the same color Color.Magenta.
Pay attention that the key point is using Magenta as TransparencyKey and BackColor. For example, if you use Red, it makes the form transparent but doesn't make it click through.
If you have some controls on your form, they will remain visible and will receive clicks. If you need to make them invisible, you can simply set Visible property of them to false
Make Normal
To make that form normal, it's enough to set BackColor to another color different than TransparencyKey, for example SystemColors.Control
Related
I am trying to make a makeshift onscreen keyboard for Windows 10 and need the background to be transparent, to make it more convenient for the user (the keys are already transparent). I, however, have no idea how to make the background transparent.
Any help would be greatly appreciated.
I believe that I am essentially looking for an updated version of the code in this thread show below:
using System;
using System.Runtime.InteropServices;
using UnityEngine;
public class TransparentWindow : MonoBehaviour
{
[SerializeField]
private Material m_Material;
private struct MARGINS
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
[DllImport("user32.dll")]
static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
static extern int SetLayeredWindowAttributes(IntPtr hwnd, int crKey, byte bAlpha, int dwFlags);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
private static extern int SetWindowPos(IntPtr hwnd, int hwndInsertAfter, int x, int y, int cx, int cy, int uFlags);
[DllImport("Dwmapi.dll")]
private static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins);
const int GWL_STYLE = -16;
const uint WS_POPUP = 0x80000000;
const uint WS_VISIBLE = 0x10000000;
const int HWND_TOPMOST = -1;
void Start()
{
// You really don't want to enable this in the editor, but it works there..
int fWidth = Screen.width;
int fHeight = Screen.height;
var margins = new MARGINS() { cxLeftWidth = -1 };
var hwnd = GetActiveWindow();
SetWindowLong(hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
// Transparent windows with click through
SetWindowLong(hwnd, -20, 524288 | 32);//GWL_EXSTYLE=-20; WS_EX_LAYERED=524288=&h80000, WS_EX_TRANSPARENT=32=0x00000020L
SetLayeredWindowAttributes(hwnd, 0, 255, 2);// Transparency=51=20%, LWA_ALPHA=2
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, fWidth, fHeight, 32 | 64); //SWP_FRAMECHANGED = 0x0020 (32); //SWP_SHOWWINDOW = 0x0040 (64)
DwmExtendFrameIntoClientArea(hwnd, ref margins);
}
void OnRenderImage(RenderTexture from, RenderTexture to)
{
Graphics.Blit(from, to, m_Material);
}
}
The code given did not work, so I assume that it is outdated. I have no idea how to update it myself, since it is a bit out of my skill set. When I upload the code to Unity, it just says that there are errors in the code and that it is not a valid script. When I open the script, however, no errors appear.
I expect to be able to have a relatively good view of whatever is behind my keyboard, like my desktop, but I actually just see a black plane.
Update:
So apparently the error message was caused by my script not having the same name as my class. I spent over 4 hours yesterday trying to fix that error message, and this naming incident was the cause :(. Thanks Ruzihm. Anyway, now that the error message is gone, when I run or build the program, my transparent window material just comes up: a dark pink. I then changed my Unity version back to 2018.2.16f1, with no success. Then I removed the #if !Unity Editor line to get the transparency to work perfectly in the editor, but not when I build it. Note, click through does work when I build it and when I run it in the editor.
As discovered in the comments, the problem was fixed when the camera's clear flags were set to solid color and the pink transparent window material was replaced a with white transparent material.
I have an old color picker utility written c++ that I coded years back and want to rewrite using c#.
I implemented the global hook to pick pixels off the screen and so on. Everything is ok but...
The cross cursor reverts to the pointer once the mouse moves outside the form and onto the desktop. This does not happen with my c++ code (MFC actually).
How is this accomplished in c#?
Thank you all.
(I'm using this http://www.codeproject.com/Articles/7294/Processing-Global-Mouse-and-Keyboard-Hooks-in-C for the hook)
The solution (or a workaround) is to simulate the first part of a mouse click event. This will lock the mouse on the calling window, thus preserving the chosen cursor.
[DllImport( "user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall )]
public static extern void mouse_event( uint dwFlags, uint dx, uint dy, uint cButtons, uint 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;
And then after enabling the mouse capture in the code:
mouse_event(
MOUSEEVENTF_LEFTDOWN,
(uint)Cursor.Position.X,
(uint)Cursor.Position.Y,
0,
0 );
this.Cursor = Cursors.Cross;
Hope it helps.
i was picked some code, implement a Topmost, Transparent and Click-Though Form via code:
public enum GWL
{
ExStyle = -20
}
public enum WS_EX
{
Transparent = 0x20,
Layered = 0x80000
}
public enum LWA
{
ColorKey = 0x1,
Alpha = 0x2
}
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
public static extern int GetWindowLong(IntPtr hWnd, GWL nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
public static extern int SetWindowLong(IntPtr hWnd, GWL nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
public static extern bool SetLayeredWindowAttributes(IntPtr hWnd, int crKey, byte alpha, LWA dwFlags);
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
int wl = GetWindowLong(this.Handle, GWL.ExStyle);
wl = wl | 0x80000 | 0x20;
SetWindowLong(this.Handle, GWL.ExStyle, wl);
SetLayeredWindowAttributes(this.Handle, 0, 128, LWA.Alpha);
}
With this code, all of form are transparent and click-though absolutely (Text, image, control button...)
Current Form Border Style is none (no border, no title bar). Now I want to make a custom Title bar, allow user move Form to other position on screen (Like Title bar).
The problems here, all of form is cannot click on it (cause by click-though code), how to do the Click-though Form but except that custom Title Bar?
Custom title bar must be transparent as other element.
I don't want to keep original Title bar, it look bad for my application.
Using the WS_EX_LAYERED style means that you have to draw your form yourself with only the UpdateLayeredWindow function. You can still get all the messages from your controls, providing that you have already paint there with UpdateLayeredWindow.
UpdateLayeredWindow will draw an hdc: handle device context only, so if you want to draw an image:
IntPtr screenHdc = GetDC(IntPtr.Zero);
IntPtr bmpHdc = CreateCompatibleDC(screenHdc);
IntPtr hBitmap;
Bitmap bmp = .... //load your bitmap, a png one for transparency
hBitmap = bmp.GetHbitmap(Color.FromArgb(0)); //Zero: background is transparent
SelectObject(bmpHdc, hBitmap);
UpdateLayeredWindow(this.Handle, screenHdc, ..., ..., bmpHdc, ..., ..., ..., ...);
Take a look UpdateLayeredWindow function for more information
I am try to get form2 positioned relative to form1. I've tried many things an nothing seems to work right. I wanted to try:
http://www.pinvoke.net/default.aspx/user32/MoveWindow.html
As a newbie to windows programming especially C# I'm looking at the syntax/example and I find it difficult to know what to put in for the parameters. I did get a different simpler p/invoke to work:
using System.Runtime.InteropServices;
...
public partial class Form1 : Form
{
public Form1()
{ InitializeComponent(); }
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CreateDirectory(string lpPathName,
IntPtr lpSecurityAttributes);
private void Form1_Load(object sender, EventArgs e) { }
private void button1_Click(object sender, EventArgs e)
{ CreateDirectory(#"c:\test4", IntPtr.Zero); }
}
...
I'm taking a guess IntPtr is "saying" I'm pointing at the first node - but only a guess...
The C# signature for MoveWindow:
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
there's comments on this as well on the site. "IntPtr hWnd" - I need to get that associated with Form2 (?) , do I repaint? I'm trying to show I've looked at it and tried to figure it out - I know we are getting it from the system's dll's...the x-y I got but getting it "with" Form2 I'm lost. Help appreciated.
In general you wouldn't need PInvoke for something as simple as this.
As long as you have a reference to form2 from form1 then you can easily do this by listening to the LocationChanged event of form1. When form1 moves then you can move form2 by doing the following:
var location = this.Location;
location.Offset(xoffset, yoffset);
form2.Location = location;
That would normally be enough to make sure form2 is placed somewhere relatively to form1 and that its position is updated when form1 is moved. You may have to set an initial position of form2 if the LocationChanged event is not called when the form is first created.
Something like this should work. Tested too. You can alter this to fit exactly what you wanted to do, which shouldn't be an issue at all.
// Win32 RECT
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
// GetWindowRect gets the win32 RECT by a window handle.
[DllImport("user32.dll", SetLastError=true)]
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
// MoveWindow moves a window or changes its size based on a window handle.
[DllImport("user32.dll", SetLastError = true)]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
// MoveForms moves one form to another using the win api.
void MoveForms(Form fromForm, Form toForm)
{
IntPtr hWnd_from = fromForm.Handle; // fromForm window handle
IntPtr hWnd_to = toForm.Handle; // toForm window handle
RECT rect_from, rect_to; // RECT holders for fromForm and toForm
if (GetWindowRect(hWnd_from, out rect_from) &&
GetWindowRect(hWnd_to, out rect_to)) // if it gets the win32 RECT for both the fromForm and toForm do the following ...
{
int x_to = rect_to.Left; // toForm's X position
int y_to = rect_to.Top; // toForm's Y position
int width_from = rect_from.Right - rect_from.Left; // fromForm's width
int height_from = rect_from.Bottom - rect_from.Top; // fromForm's height
// Moves fromForm to toForm using the x_to, y_to for X/Y and width_from, height_from for W/H.
MoveWindow(hWnd_from, x_to, y_to, width_from, height_from, true);
}
}
Is there any way I can send a left click event to a TextBox? for what ever the reason although I am doing a TextBox.Focus() and the cursor is blinking inside the TextBox but I cannot start typing in it, But if I do an extra click with the mouse inside the text box then I can start typing. So, I was wondering how to send that event to it?
To send mouse events, you'll need to import user32.dll and use mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
Example
class Mouse
{
[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; //Left click
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08; //Right click
private const int MOUSEEVENTF_RIGHTUP = 0x10;
public static void sendMouseRightclick(Point p)
{
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, p.X, p.Y, 0, 0); //Sends a mouse right click at the specified Point
}
public static void sendMouseClick(Point p)
{
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, p.X, p.Y, 0, 0); //Sends a mouse left click at the specified Point
}
}
When using this, you may call Mouse.sendMouseClick(Point p) to send a mouse left click at the specified point.
In your case, I think that you might want to use Mouse.sendMouseClick(TextBox.Location); to send a mouse left click at the current TextBox position.
Thanks,
Have a great day :)
Ok to fix this first we should make sure the form itsself is activate.
To do that, in the Form_Shown event we should call this.Activate()