11

Thank you for previous answers that enabled to me complete the basic tool that shows large red cross in the mouse coordinates in order to let be more visible. The red cross is an image with transparent background in the transparent form. The problem is that you cannot click through, since its topmost and the center of form is actually positioned to mouse xy. Is there any way how to make this usable in order to have the cross still displayed on the cursor but "clickable" through?

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
Petr
  • 183
  • 1
  • 3
  • 9

2 Answers2

12

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;
Joey
  • 344,408
  • 85
  • 689
  • 683
  • Please could you provide me a bit more details? Im not experienced in this low level API, thank you! – Petr Oct 06 '09 at 07:18
  • Thank you very much, this is really awesome place, such quick answers. – Petr Oct 06 '09 at 07:24
  • With the SetLayeredWindowAttributes the transparency, previously set in VS Designer, get lost and the form was semitransparent. But disabling this line doesnt affect the "clickable" effects, it works great! – Petr Oct 06 '09 at 07:31
  • BTW, why I need to set transparency? It works great with //added before SetLayeredWindowAttributes(this.Handle, 0, 128, LWA.Alpha); – Petr Oct 06 '09 at 07:39
  • Yes, you can remove that part. It's just that I needed a partially transparent window at that time. I think the important part for WS_EX_TRANSPARENT to work is that you set the WS_EX_LAYERED window style too, even though you don't have to set a transparency value. – Joey Oct 06 '09 at 07:42
  • 1
    This is the right style to use, but don't apply it this way. Override [`CreateParams`] instead. http://stackoverflow.com/q/13986363/1386111 – Ben Voigt Sep 06 '13 at 14:17
  • @Joey is it possible to do this with a picture that has a transparent background? e.g. a circle? – hurnhu Jul 27 '14 at 23:26
  • @QuolonelQuestions: Most likely not, no. Maybe in WPF you can turn hit-testing off for a window and it does the correct thing, but Windows Forms won't grow new features and since it's only a thin wrapper around Win32 you often have to go the P/Invoke route for things that are not supported directly. – Joey Nov 21 '16 at 11:00
  • "Windows Forms won't grow new features" Why? – Quolonel Questions Nov 21 '16 at 22:17
0

To provide a more detailed/commented version, which also uses TransparencyKey as transparency key (not black as the version above), and one might set _alpha as desired.

        /// <summary>
        /// 0: the window is completely transparent ... 255: the window is opaque
        /// </summary>
        private byte _alpha;

        private enum GetWindowLong
        {
            /// <summary>
            /// Sets a new extended window style.
            /// </summary>
            GWL_EXSTYLE = -20
        }

        private enum ExtendedWindowStyles
        {
            /// <summary>
            /// Transparent window.
            /// </summary>
            WS_EX_TRANSPARENT = 0x20,
            /// <summary>
            /// Layered window. http://msdn.microsoft.com/en-us/library/windows/desktop/ms632599%28v=vs.85%29.aspx#layered
            /// </summary>
            WS_EX_LAYERED = 0x80000
        }

        private enum LayeredWindowAttributes
        {
            /// <summary>
            /// Use bAlpha to determine the opacity of the layered window.
            /// </summary>
            LWA_COLORKEY = 0x1,
            /// <summary>
            /// Use crKey as the transparency color.
            /// </summary>
            LWA_ALPHA = 0x2
        }

        [DllImport("user32.dll", EntryPoint = "GetWindowLong")]
        private static extern int User32_GetWindowLong(IntPtr hWnd, GetWindowLong nIndex);

        [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
        private static extern int User32_SetWindowLong(IntPtr hWnd, GetWindowLong nIndex, int dwNewLong);

        [DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
        private static extern bool User32_SetLayeredWindowAttributes(IntPtr hWnd, int crKey, byte bAlpha, LayeredWindowAttributes dwFlags);

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            //Click through
            int wl = User32_GetWindowLong(this.Handle, GetWindowLong.GWL_EXSTYLE);
            User32_SetWindowLong(this.Handle, GetWindowLong.GWL_EXSTYLE, wl | (int)ExtendedWindowStyles.WS_EX_LAYERED | (int)ExtendedWindowStyles.WS_EX_TRANSPARENT);
            //Change alpha
            User32_SetLayeredWindowAttributes(this.Handle, (TransparencyKey.B << 16) + (TransparencyKey.G << 8) + TransparencyKey.R, _alpha, LayeredWindowAttributes.LWA_COLORKEY | LayeredWindowAttributes.LWA_ALPHA);
        }
xamid
  • 440
  • 11
  • 25