8

I'm making a little tool for drawing onto the screen with the mouse after a 'pen' button is toggled in a floating sidebar.

I have done this (please don't laugh) by having a top-most windows form with its background as its transparency key cover the whole screen. I need to make the mouse not click through the form onto the stuff below when I"m in drawing mode. I tried following this: Windows form with a transparent background that cannot be clicked through How to disable click through on transparent control?

which successfully stops the mouse but also un-maximises the form and drags it around with the mouse (using HTCAPTION IntPtr(2) this is) I tried using some of the other values listed on MSDN, but with no luck.

I'm way out of my depth, any help greatly appreciated (newbie friendly please!)

PS I'm using this right now..

        //code for allowing clicking through of menus
        protected override void WndProc(ref Message m)
        {              
            if (penMode && m.Msg == 0x84)
            {
                m.Result = new IntPtr(2);    
            }
            else
                base.WndProc(ref m);
        }

UPDATE: Now solved the problem by approaching it in another way entirely. It doesn't look like WndProc will work so I simply created a blank form over the whole screen the showed my main form (form.Show(this)) from within that. Then adjust the opacity of the blank form which sits underneath from 0% to 1% to allow/ prevent clicking through. Works! Thanks to all answers, taught me a lot.

Community
  • 1
  • 1
Mr Pie
  • 327
  • 2
  • 11

2 Answers2

8

Actually, no need to laugh—it sounds to me like you're doing this the correct way already. Since you don't own the desktop, you shouldn't draw directly on it. Instead, you need to simulate it by overlaying a transparent form that you do own, and then drawing on that. Because you own the transparent overlay form, it's no problem to draw on it.

But beyond that, it sounds like you're just trying values randomly without a clear understanding of what they actually do. That's like throwing darts with your eyes closed. You won't have a very high hit count.

Let's start by understanding what your code does. The magic value 0x84 corresponds to the WM_NCHITTEST message, which is sent by Windows to a window to determine how mouse clicks on that window should be handled. In response to that message, you reply with one of the HT* values, given in the linked documentation. Each of those values has a particular meaning, also explained in the documentation. For example:

  • HTCAPTION (which has a value of 2) means that the clicked portion of the window should be treated as the window's caption/title bar. You know from using Windows that you can drag windows around on the screen using the title bar, so it makes sense that returning HTCAPTION in response to mouse clicks would allow your window to be draggable. You'll see this used on borderless forms (i.e., those with no title bar) to allow them to be movable.

  • HTTRANSPARENT (which has a value of -1) is another available value. This one's pretty simple. It just makes your window look transparent. It's like saying "don't mind me, there's no window here!" Mouse clicks are simply passed on to the window that lies below yours in the Z order as if you weren't there.

  • HTCLIENT (a value of 1) is the default result when the click occurs anywhere on the window's client area. You would return this (or simply call the default window procedure) when you want everything to work normally. Click events that return this value would go on to be processed normally by the framework, raising either the form's Click event, or getting passed on to child controls located on the form.

So, when you're not drawing, you probably want to return HTTRANSPARENT. When you are drawing, you probably want to return HTCLIENT so that your drawing code can see the mouse events and draw the result.

Fixing your code, then:

// Code for allowing clicking through of the form
protected override void WndProc(ref Message m)
{
    const uint WM_NCHITTEST = 0x84;

    const int HTTRANSPARENT = -1;
    const int HTCLIENT      = 1;
    const int HTCAPTION     = 2;
    // ... or define an enum with all the values

    if (m.Msg == WM_NCHITTEST)
    {
        // If it's the message we want, handle it.
        if (penMode)
        {
            // If we're drawing, we want to see mouse events like normal.
            m.Result = new IntPtr(HTCLIENT);
        }
        else
        {
            // Otherwise, we want to pass mouse events on to the desktop,
            // as if we were not even here.
            m.Result = new IntPtr(HTTRANSPARENT);
        }
        return;  // bail out because we've handled the message
    }

    // Otherwise, call the base class implementation for default processing.
    base.WndProc(ref m);
}
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 1
    Testing on a windows 8 machine, the transparent part of the window does not send messages to the parent window when the mouse is clicked. (Verified in logging the messages and in Spy++). – John Koerner Apr 07 '13 at 13:27
  • @Cody, wow, thankyou so much for such a clear and complete answer! It looks good, but what I'm now seeing is that after drawing a point, if I again click on that point, which is now blue, not transparent, the click goes through.. – Mr Pie Apr 07 '13 at 14:09
  • 1
    Doesn't work on Windows 10 for the same reason John mentioned. – Professor of programming Feb 28 '16 at 13:44
  • Can't help you with that, @bonner. I don't have Windows 8 or 10 installed anywhere. Not a big priority, I don't have a touch screen attached to my computer. – Cody Gray - on strike Feb 28 '16 at 13:47
  • The solution ChronosMOT suggested works, despite being a hacky workaround. – Professor of programming Feb 28 '16 at 15:20
2

You might just want to set the visibility of your window to like 5% or so and leave the transparent key deactivated.

you basically won't notice it and jet it's there :D

hope this helps

ChronosMOT
  • 341
  • 5
  • 17
  • Thank you @Chronos, but is there any way to do this where the buttons and more importantly the stuff that I draw with the pen mode are not 5% visible? – Mr Pie Apr 08 '13 at 03:03
  • 2
    I guess I underestimate this problem. I have a solution using three forms now. the first just showing the gui beeing mostly invisible thanks to transparency key. the second one only controlling the input with a visibility of 5%. and the third getting drawn on using transparency key again. Sry I didn't think this through earlier. This time tested it here are the files as well as a .rar containing all of them https://drive.google.com/folderview?id=0B-5Qr5swDDp8ZV9CMUVrVHloeWc&usp=sharing – ChronosMOT Apr 08 '13 at 13:43
  • Thanks, I have since come up with a similar solution using two forms - I draw on the same one as the UI, I didn't update the solution yet as I haven't been able to fully test in on all target machines yet. Glad to see I'm thinking along the same lines as someone else! – Mr Pie Apr 09 '13 at 03:24