Transparent window layer that is click-through and always stays on top
Here's a refined full sample code for making a window topmost - click through - transparent (= alpha blended). The sample makes a rotating color wheel which is rendered with DirectX, or actually with XNA 4.0, because I believe Microsoft has discontinued developing the managed directx and favours XNA today.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework.Graphics;
namespace ClickThroughXNA
{
public partial class Form1 : Form
{
// Directx graphics device
GraphicsDevice dev = null;
BasicEffect effect = null;
// Wheel vertexes
VertexPositionColor[] v = new VertexPositionColor[100];
// Wheel rotation
float rot = 0;
public Form1()
{
InitializeComponent();
StartPosition = FormStartPosition.CenterScreen;
Size = new System.Drawing.Size(500, 500);
FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; // no borders
TopMost = true; // make the form always on top
Visible = true; // Important! if this isn't set, then the form is not shown at all
// Set the form click-through
int initialStyle = GetWindowLong(this.Handle, -20);
SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20);
// Create device presentation parameters
PresentationParameters p = new PresentationParameters();
p.IsFullScreen = false;
p.DeviceWindowHandle = this.Handle;
p.BackBufferFormat = SurfaceFormat.Vector4;
p.PresentationInterval = PresentInterval.One;
// Create XNA graphics device
dev = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, p);
// Init basic effect
effect = new BasicEffect(dev);
// Extend aero glass style on form init
OnResize(null);
}
protected override void OnResize(EventArgs e)
{
int[] margins = new int[] { 0, 0, Width, Height };
// Extend aero glass style to whole form
DwmExtendFrameIntoClientArea(this.Handle, ref margins);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// do nothing here to stop window normal background painting
}
protected override void OnPaint(PaintEventArgs e)
{
// Clear device with fully transparent black
dev.Clear(new Microsoft.Xna.Framework.Color(0, 0, 0, 0.0f));
// Rotate wheel a bit
rot+=0.1f;
// Make the wheel vertexes and colors for vertexes
for (int i = 0; i < v.Length; i++)
{
if (i % 3 == 1)
v[i].Position = new Microsoft.Xna.Framework.Vector3((float)Math.Sin((i + rot) * (Math.PI * 2f / (float)v.Length)), (float)Math.Cos((i + rot) * (Math.PI * 2f / (float)v.Length)), 0);
else if (i % 3 == 2)
v[i].Position = new Microsoft.Xna.Framework.Vector3((float)Math.Sin((i + 2 + rot) * (Math.PI * 2f / (float)v.Length)), (float)Math.Cos((i + 2 + rot) * (Math.PI * 2f / (float)v.Length)), 0);
v[i].Color = new Microsoft.Xna.Framework.Color(1 - (i / (float)v.Length), i / (float)v.Length, 0, i / (float)v.Length);
}
// Enable position colored vertex rendering
effect.VertexColorEnabled = true;
foreach (EffectPass pass in effect.CurrentTechnique.Passes) pass.Apply();
// Draw the primitives (the wheel)
dev.DrawUserPrimitives(PrimitiveType.TriangleList, v, 0, v.Length / 3, VertexPositionColor.VertexDeclaration);
// Present the device contents into form
dev.Present();
// Redraw immediatily
Invalidate();
}
[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("dwmapi.dll")]
static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref int[] pMargins);
}
}
Windows Forms: Pass clicks through a partially transparent always-on-top window
You can make a window, click-through by adding WS_EX_LAYERED
and WS_EX_TRANSPARENT
styles to its extended styles. Also to make it always on top set its TopMost
to true
and to make it semi-transparent use suitable Opacity
value:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Opacity = 0.5;
this.TopMost = true;
}
[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);
const int GWL_EXSTYLE = -20;
const int WS_EX_LAYERED = 0x80000;
const int WS_EX_TRANSPARENT = 0x20;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
var style = GetWindowLong(this.Handle, GWL_EXSTYLE);
SetWindowLong(this.Handle,GWL_EXSTYLE , style | WS_EX_LAYERED | WS_EX_TRANSPARENT);
}
}
Sample Result
Topmost form, clicking through possible?
You can use SetWindowLong to set the WS_EX_TRANSPARENT window style:
If the layered window has the WS_EX_TRANSPARENT extended window style, the shape of the layered window will be ignored and the mouse events will be passed to the other windows underneath the layered window.
CodeProject has this article detailing the technique. Though it's in VB.NET it should be easy to convert to C#.
I have used the following code in the past:
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);
}
but it also was copied from somewhere else. The important lines here are in the OnShown
method. Though I have to admit that the line
wl = wl | 0x80000 | 0x20;
is a little cryptic, setting the WS_EX_LAYERED and WS_EX_TRANSPARENT extended styles.
You can probably also set it like
wl = wl | WS_EX.Layered | WS_EX.Transparent;
Transparent Windows Form that can handle Click
If I understand your question correctly, you can Use TrancparencyKey
Set TrancparencyKey
and BackColor
properties both to same color like Color.Red
.
Here is the screenshot of transparent form over visual studio:
Note:
- When you use for example
Color.Red
every thing works fine and you can handle mouseClick
. But the behavior is different for different colors, for exampleColor.Magenta
the form can not capture the mouseClick
.
Click-through Transparent window, no dragging allowed [C++]
The click-through part:
Indeed, WS_EX_TRANSPARENT
by itself is a big lie; so I used WS_EX_COMPOSITED | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST
instead.
I have control over the opacity using SetLayeredWindowAttributes(hWnd, 0, (255 * opacity) / 100, LWA_ALPHA);
(quite unorthodox, but it works) and I also use
SetCapture(hWnd);
ShowCursor(false);
to grab the mouse focus as the top level window doesn't let go and hides the cursor.
I also tried to force the focus on the window adding WM_NCACTIVATE
and WM_ACTIVEAPP
:
case WM_MOUSEMOVE:
fprintf(stdout, "Mouse move [%d][%d]\n", GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
SetForegroundWindow(hWnd);
break;
case WM_LBUTTONDOWN:
printf("Mouse click\n");
SetForegroundWindow(hWnd);
break;
case WM_NCACTIVATE:
return false;
case WM_ACTIVATEAPP:
wActive = (bool)wParam;
if(wActive == false)
return 0;
else
return DefWindowProc(hWnd, message, wParam, lParam);
The dragging part:
In my particular case I wanted to 'poke' the window underneath (child window) without losing focus; unfortunately, any mouse click event will change the focus to that child window - a solution would be to:
- set a timer (
SetTimer
,WM_TIMER
) and check whether your application lost focus or not - set a hook to your window and reply to a
WM_KILLFOCUS
message with aWM_SETFOCUS
message
click through transparent window - does not work on all computers
the following solves my problem thanks to Jimi's comments:
Protected Overrides ReadOnly Property CreateParams As CreateParams
Get
Dim parms As CreateParams = MyBase.CreateParams
If Not DesignMode Then parms.ExStyle = parms.ExStyle Or &H80000 Or &H20
Return parms
End Get
End Property
Private Sub frmDrawing_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim InitialStyle As Integer
'Dim PercentVisible As Decimal
InitialStyle = GetWindowLong(Me.Handle, -20)
'PercentVisible = 0.4
Dim parms As CreateParams = MyBase.CreateParams
'parms.ExStyle = parms.ExStyle Or &H80000 Or &H20
'SetWindowLong(Me.Handle, -20, InitialStyle Or &H80000 Or &H20)
'MsgBox(CStr(parms.ExStyle) & ":" & CStr(InitialStyle Or &H80000 Or &H20))
SetWindowLong(Me.Handle, -20, parms.ExStyle)
'SetLayeredWindowAttributes(Me.Handle, 0, 255 * PercentVisible, &H2)
Me.BackColor = System.Drawing.Color.LightBlue
Me.TransparencyKey = System.Drawing.Color.LightBlue
Me.TopMost = True
End Sub
Related Topics
Pass Multiple Complex Objects to a Post/Put Web API Method
Assert an Exception Using Xunit
How to Do Tostring for a Possibly Null Object
How to Compare Lists in Unit Testing
Visual Studio 2015 Break on Unhandled Exceptions Not Working
Catching Exceptions with "Catch, When"
Getting Content/Message from Httpresponsemessage
Proper Datagrid Search from Textbox in Wpf Using Mvvm
Are Empty Interfaces Code Smell
Linq Syntax - Selecting Multiple Columns
Resize Wpf Window and Contents Depening on Screen Resolution
Ensure That Httpconfiguration.Ensureinitialized()
Cannot Convert Lambda Expression to Type 'String' Because It Is Not a Delegate Type
Converting an Int[] to Byte[] in C#
Referenced Project Gets "Lost" at Compile Time