Click here to Skip to main content
Click here to Skip to main content
Go to top

Transparent, Click-Through Forms

, 7 Sep 2007
Rate this:
Please Sign up or sign in to vote.
How to make a form transparent to the mouse, or click-through, so that mouse clicks end up going to whatever is behind the transparent form.
ClickThrough Window screenshot

Introduction

Have you ever wondered how to make a form ignore the mouse so the clicks get sent to the application, or whatever else, is behind your form? Perhaps you want to create an application that overlays a window with one of your own, but you wanted your window to be "transparent" to the mouse. It's pretty easy if you know the correct attributes and how to apply them to your form! The trick lies in "Layered Windows". I'll discuss how these Layered Windows work, and how to use them to pull off a "click-through" window in your Windows Forms applications.

Layered Windows

With the introduction of Windows 2000, the User Interface (UI) got a much needed face-lift through prettier graphics and ease-to-use improvements. You may have noticed some of these improvements, like the little shadow that now lies under the mouse pointer, fade-in and -out menus and ToolTips, alpha-blended dragging of objects around the Shell (Explorer), non-rectangular window shapes, and visually transparent (or alpha-blended) windows, to name a few. All these UI effects, and more, were made possible with the introduction of a new extended window style, called Layered Windows, in Windows 2000 Beta 3.

When a window is created, it has a standard set of attributes attached to it, like its name, position, size, style, and window handle, among others. Most of these attributes can be set or reset at any time, either during or after the window is created, by calling the Win32 API functions GetWindowLong and SetWindowLong. I'll show you how to use these functions to set the WS_EX_LAYERED and WS_EX_TRANSPARENT window extended attributes to turn your .NET Framework Form windows into Layered Windows. I'll also show you how to use the SetLayeredWindowAttribute function to change the opacity of the window to make them "see-through" as well as "click-through".

Transparent Windows

The WS_EX_TRANSPARENT extended window style bit is poorly documented. Its definition in the documentation on MSDN says that all windows beneath this window will not be obscured by this window. It goes on to say that this window will also get the Paint message (WM_PAINT) only after all the sibling windows (in the same application) beneath it have received and processed their Paint messages. What on earth does that mean? Well, it's not defined anywhere. That's the only description of what this bit does. It takes some trial and error to figure out what's really going on.

With some playing around, you can discover that windows, with the Transparency bit set, are invisible to mouse clicks! It's supposed to be not just anywhere you click on a form (window). Take the example of a window with a random blob shape, sort of like a cloud. Well, with normal, plain-old windows, every window is rectangular in shape. Everywhere you click between the top-left corner and bottom-right corner of a window falls within the bounds of that window. It's very easy to determine which window actually got clicked on. But, in our cloud-shaped window, there is no usable bounds to check where the mouse clicked. The window "frame" is a random line that can't be described in a simple mathematical formula. Without that simple formula, trying to figure out if the mouse click actually hit inside the cloud-shaped form can be a huge computational task! But there is an easier method.

A layered window, when drawn, is not drawn directly to the screen. It's drawn to an off-screen buffer and converted to a bitmap that uses an Alpha Channel. What's that? An Alpha channel basically describes which pixels in an image are visibly transparent and how much the background shows through those pixels. For example, in a 32-bit image, you can have Red, Green, and Blue color values, each 8-bits wide, that describe the color of a single pixel at any point in the image. You can also have an 8-bit value describing how much that pixel is visibly transparent. This says how much of the color that's behind that pixel, when drawn to the screen, comes through and affects the corresponding pixel in the image. When the window is finished drawing to this off-screen buffer, the completed buffer image is copied to the visible screen, pixel-by-pixel, remembering to take into account the alpha values at each pixel location.

Now, how does the Alpha channel help in determining if the form was hit by a mouse click or not? Well, in the off-screen buffer, the form still occupies a rectangular area of the screen. It just has an oddly-shaped, alpha-blended image inside it. Some parts of the image will have an alpha value of 255 (not visibly transparent at all) down to 0 (completely transparent). This is what gives our form its shape on the visible screen. The Alpha channel can be checked quite easily and quickly, just like in a rectangular form, to see if the mouse hit an area of the form that is visible or not. If the alpha value at the point that the mouse was clicked (mapped to the off-screen buffer) is less than 255, the form was missed and the mouse click goes to the window that is under our window! Simple, isn't it? But, that's not how it works in practice. Setting the Transparent bit turns the entire form invisible to the mouse, "no matter what the Alpha channel value says" is supposed to happen at the spot the mouse is clicked! It appears that a form is transparent, "all or nothing"!

A Transparent Window Example

In order to get a form to be "click-throughable", you have to modify the Extended Style attributes of the window that is your form. How do we do that? We'll start with a small wrapper library.

The following library is pretty standard, just quick-and-dirty stuff. No error checking and just the bare-bone things we need to get the job done. It just defines the constants and functions we need to call Get/SetWindowLong and SetLayeredWindowAttributes. You can find the same things on PInvoke.net, or derive it yourself from the Platform SDK documentation.

Imports System.Runtime.InteropServices

Public Class User32Wrappers

    Public Enum GWL As Integer
        ExStyle = -20
    End Enum

    Public Enum WS_EX As Integer
        Transparent = &H20
        Layered = &H80000
    End Enum

    Public Enum LWA As Integer
        ColorKey = &H1
        Alpha = &H2
    End Enum

    <DllImport("user32.dll", EntryPoint:="GetWindowLong")> _
    Public Shared Function GetWindowLong( _
        ByVal hWnd As IntPtr, _
        ByVal nIndex As GWL _
            ) As Integer
    End Function

    <DllImport("user32.dll", EntryPoint:="SetWindowLong")> _
    Public Shared Function SetWindowLong( _
        ByVal hWnd As IntPtr, _
        ByVal nIndex As GWL, _
        ByVal dwNewLong As WS_EX _
            ) As Integer
    End Function

    <DllImport("user32.dll", _
      EntryPoint:="SetLayeredWindowAttributes")> _
    Public Shared Function SetLayeredWindowAttributes( _
        ByVal hWnd As IntPtr, _
        ByVal crKey As Integer, _
        ByVal alpha As Byte, _
        ByVal dwFlags As LWA _
            ) As Boolean
    End Function
End Class

Truth be told, this trick is ridiculously easy to pull off. To make a form "click-through", all you need to do is:

  1. Grab the current value of the Extended Style attributes for the window you want to be invisible to the mouse. This requires a call to the Win32 API function GetWindowLong(hWnd, nIndex). The two parameters are as follows:
    • hWnd - The handle to the window we want to get the attributes for
    • nIndex - The zero-based offset to the value to be retrieved. In our case, GWL_EXSTYLE, or -20
  2. Modify the value returned by GetWindowLong() and turn on the bits you want. This is done using a bit-wise OR operation. We want to turn on the bits specified by WS_EX_LAYERED and WS_EX_TRANSPARENT.
    newValue = oldValue Or WS_EX_LAYERED Or WS_EX_TRANSPARENT
  3. Write the new value back to the window by calling SetWindowLong(hWnd, nIndex, dwNewLong). The first two are exactly the same as the call to GetWindowLong(). The third is, obviously, the new value we want written to the window.
  4. Now that the window is a Layered Window, we have to modify another attribute of the form, the Alpha attribute. If this is not done, the default value for Alpha will be 0, or visibly transparent, and we won't see our form at all! This is accomplished with a call to SetLayeredWindowAttributes(hWnd, crKey, bAlpha, dfFlags).
    • hWnd - The handle to the window we want to set the attribute for
    • crRef - Specifies a transparency color key. All pixels with this color in the form will be transparent
    • bAlpha - Specifies the opacity of the window, 0 (transparent) through 255 (opaque)
    • dwFlags - Specifies an action to take
    • LWA_COLORKEY - Uses crKey as the transparency color
    • LWA_ALPHA - Uses bAlpha to determine the opacity of the window

In the following example, the entire form is going to have its Alpha changed, not just parts of it, so we won't be using a color key. Putting the above wrapper library to use:

Imports WindowLibrary.User32Wrappers

Public Class Form1

    Private _InitialStyle As Integer

    Private Sub Form_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) _
        Handles MyBase.Load
        ' Grab the Extended Style information
        ' for this window and store it.
        _InitialStyle = GetWindowLong(Me.Handle, GWL.ExStyle)

        ' Set this window to Transparent
        ' (to the mouse that is!)
        SetFormToTransparent()

        ' Just for giggles, set this window
        ' to stay on top of all others so we
        ' can see what's happening.
        Me.TopMost = True
    End Sub

    Private Sub SetFormToTransparent()
        ' This creates a new Extended Style
        ' for our window, which takes effect
        ' immediately upon being set, that
        ' combines the initial style of our window
        ' (saved in Form.Load) and adds the ability
        ' to be Transparent to the mouse.
        ' Both Layered and Transparent must be
        ' turned on for this to work AND have
        '  the window render properly!
        SetWindowLong(Me.Handle, GWL.ExStyle, _
            _InitialStyle Or WS_EX.Layered Or WS_EX.Transparent)

        ' Don't forget to set the Alpha
        ' for the window or else you won't be able
        ' to see the window! Possible values
        ' are 0 (visibly transparent)
        ' to 255 (visibly opaque). I'll set
        ' it to 70% visible here for show.
        ' The second parameter is 0, because
        ' we're not using a ColorKey!
        SetLayeredWindowAttributes(Me.Handle, 0, _
                           255 * 0.7, LWA.Alpha)
    End Sub

    Private Sub SetFormToOpaque()
        ' Turn off the Transparent Extended Style.
        SetWindowLong(Me.Handle, GWL.ExStyle, _
                _InitialStyle Or WS_EX.Layered)

        ' Set the Alpha back to 100% opaque.
        SetLayeredWindowAttributes(Me.Handle, _
                           0, 255, LWA.Alpha)
    End Sub
End Class

The example code here is pretty simple. To see what it does, all you do is start the app and see what happens when you try to click on the form. It's easiest to see when you have a text editor window open behind this example form. It operates in two modes, Transparent and Active, and starts up in Transparent mode. If you click the mouse anywhere in the window, the click falls through to the window behind it. To make the sample window Active, make sure the form has the focus by clicking on its icon in the TaskBar, then hold down the Shift and Ctrl keys. You should see the form's opacity change. Now, any mouse clicks on the form will not fall through and you'll be able to use the form's controls.

In order to close the sample, you'll have to right-click its icon on the TaskBar and click Close.

In my next article, I'll show you how to use this technique in an overlay window that attaches itself to a "host" window, follows it, and resizes itself to match the dimensions of the host. It'll also display some information over the top of the host window and, of course, be completely invisible to the mouse.

References

History

  • Article v1.00 - Jan 29, 2006

This article was written expressly for The CodeProject. If you find this article, in part or in whole, on any other website, it is a blatent copyright violation. Please inform The CodeProject staff of this at webmaster@codeproject.com. Thank you.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Dave Kreskowiak
Systems Engineer Blue Cross Blue Shield of Michigan
United States United States
I started out as a child. First, mastering drooling and sucking on a bottle. I soon discovered that these skills came to be very important later in life, and I put them to good use. Not "over the top" mind you, as I never drove home drunk. But, that damn bib catching my drool was getting in the way and turning the ladies away, so I had to abandon my strength and develop other skills.
 
So, I entered the world of geekdom. BASIC at first, then TMS9900 and Intel Assemblers, COBOL, C, C++, ...
 
... to be continued ...

Comments and Discussions

 
SuggestionPlease do the next arcticle... PinmemberIppo00710-Mar-14 7:39 
QuestionWill this work on all windows operating systems? Pingroupvlad7817-Jun-12 20:49 
AnswerRe: Will this work on all windows operating systems? PinmvpDave Kreskowiak8-Jun-12 2:05 
I don't see why not, though I've never tried it.
 
There's only one way to tell!

GeneralRe: Will this work on all windows operating systems? Pingroupvlad7818-Jun-12 10:04 
GeneralRe: Will this work on all windows operating systems? PinmvpDave Kreskowiak8-Jun-12 10:28 
GeneralRe: Will this work on all windows operating systems? Pingroupvlad7818-Jun-12 12:11 
GeneralRe: Will this work on all windows operating systems? PinmvpDave Kreskowiak8-Jun-12 13:29 
GeneralRe: Will this work on all windows operating systems? Pingroupvlad7818-Jun-12 22:02 
GeneralMy vote of 5 Pinmembermanoj kumar choubey21-Feb-12 23:52 
GeneralMy vote of 5 Pinmemberthisisgaydoosh3-Feb-11 18:10 
Question"overlay window" article?? Pinmemberelian.notario19-Jan-11 2:41 
AnswerRe: "overlay window" article?? PinmvpDave Kreskowiak19-Jan-11 14:15 
QuestionAnyone get this to work with WPF window ? PinmemberEvan Wellens11-Jun-10 9:35 
AnswerRe: Anyone get this to work with WPF window ? PinmvpDave Kreskowiak11-Jun-10 12:05 
General.Net windows now have Opacity and TransparencyKey properties PinmemberMark Gray15-Mar-10 8:52 
GeneralRe: .Net windows now have Opacity and TransparencyKey properties PinmvpDave Kreskowiak15-Mar-10 14:14 
QuestionClick Through On Transparent Only? PinmemberJBenhart19-May-09 9:47 
AnswerRe: Click Through On Transparent Only? PinmvpDave Kreskowiak19-May-09 17:16 
GeneralRe: Click Through On Transparent Only? PinmemberJBenhart19-May-09 18:18 
GeneralRe: Click Through On Transparent Only? PinmvpDave Kreskowiak20-May-09 1:57 
GeneralRe: Click Through On Transparent Only? PinmemberJBenhart20-May-09 6:23 
GeneralRe: Click Through On Transparent Only? PinmvpDave Kreskowiak20-May-09 10:16 
QuestionMaking a form transparent , nonmodal but not click-through PinmemberMember 31043487-May-09 0:33 
AnswerRe: Making a form transparent , nonmodal but not click-through PinmvpDave Kreskowiak7-May-09 14:36 
GeneralThanks. Pinmemberflyinlonewolf4-Mar-09 20:11 
QuestionHow to port this to Visual C++? Pinmembermarcelo3924-Apr-08 4:27 
QuestionHow to capture image of click-through window Pinmembertunguyendinh8-Nov-07 0:20 
AnswerRe: How to capture image of click-through window PinmvpDave Kreskowiak8-Nov-07 3:00 
GeneralRe: How to capture image of click-through window Pinmembertunguyendinh8-Nov-07 4:30 
GeneralRe: How to capture image of click-through window PinmvpDave Kreskowiak8-Nov-07 9:38 
GeneralNext article?! Pinmemberjamesih21-May-07 19:54 
GeneralRe: Next article?! PinmvpDave Kreskowiak22-May-07 12:00 
GeneralRe: Next article?! PinmemberCharvak Karpe20-Jul-07 6:24 
GeneralRe: Next article?! PinmvpDave Kreskowiak25-Jul-07 1:49 
QuestionHow to make controllers active PinmemberMicroMicrob16-Apr-07 18:29 
AnswerRe: How to make controllers active PinmvpDave Kreskowiak17-Apr-07 1:39 
QuestionRe: How to make controllers active Pinmembervictor.sauermann2-May-07 9:55 
AnswerRe: How to make controllers active PinmvpDave Kreskowiak2-May-07 12:32 
AnswerRe: How to make controllers active Pinmemberthe one and only scottimus25-Jul-08 14:04 
QuestionHow implement your.... Pinmemberdox2219-Apr-06 9:48 
AnswerRe: How implement your.... PinmemberDave Kreskowiak19-Apr-06 11:46 
QuestionConstant Values Pinmemberhiwiller14-Feb-06 6:05 
AnswerRe: Constant Values PinmemberDave Kreskowiak14-Feb-06 12:05 
GeneralRe: Constant Values Pinmemberhiwiller14-Feb-06 12:40 
GeneralGood one PinmemberRajkumarJ31-Jan-06 16:32 
GeneralRe: Good one PinmemberDave Kreskowiak31-Jan-06 16:56 
GeneralNice trick! PinmemberMogobuTheFool31-Jan-06 11:30 
GeneralRe: Nice trick! PinmemberDave Kreskowiak31-Jan-06 17:00 
GeneralRe: Nice trick! PinmemberMogobuTheFool31-Jan-06 17:58 
GeneralRe: Nice trick! PinmemberDave Kreskowiak2-Feb-06 1:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 7 Sep 2007
Article Copyright 2006 by Dave Kreskowiak
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid