Home > Net >  WM_LBUTTONDOWN and WM_LBUTTONUP messages are not received in the WndProc for filled rectangles
WM_LBUTTONDOWN and WM_LBUTTONUP messages are not received in the WndProc for filled rectangles

Time:08-09

I have made a semi-transparent, borderless form with a resizable transparent panel in the middle. To do this, overriding the OnPaint, I painted dashed borders and boxes around the panel using e.Graphics.DrawLines(...), e.Graphics.FillRectangles(...), and e.Graphics.DrawRectangles(...) methods. I then handled the WM_NCHITTEST and WM_MOUSEMOVE in the WndProc as below.

protected override void WndProc(ref Message m)
{
    bool handled = false;

    if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
    {
        if (...) // any of the boxes contains the client-point
        {
            m.Result = (IntPtr)HT; // HT can be HTLEFT = 10, HTRIGHT = 11, and so on
            handled = true;
        }
    }

    if (!handled)
        base.WndProc(ref m);
}

The resizable transparent panel in the middle of a semi-transparent borderless form

So far so good—the dashed borders and the boxes filled with a white brush pass the hit test successfully. Now, I also need to handle the WM_LBUTTONDOWN and WM_LBUTTONUP messages in the WndProc. It seems the WndProc receives either of these messages only when the poiner is over the borders of the panel (i.e., drawn with the e.Graphics.DrawLines(...)) or the borders of the boxes (i.e., drawn with the e.Graphics.DrawRectangles(...)) but not when the mouse pointer is over the filled area of the boxes (i.e., filled with the e.Graphics.FilledRectangles(...)). Any sggestions as why this is happening?

protected override void WndProc(ref Message m)
{
    bool handled = false;

    if (m.Msg == WM_LBUTTONDOWN || m.Msg == WM_LBUTTONUP)
    {
        // unreachable for the filled rectangles!?
    }

    if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
    {
        ...
    }

    if (!handled)
        base.WndProc(ref m);
}

Here is the whole code.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Microfluidic_Experiment_Assistant
{
    public class FloatingPanel : Panel
    {
        #region __________ Declarations

        public enum SizingModes
        {
            Normal,
            GripBoxesOnly
        }

        #endregion

        #region __________ Fields

        private RectangleF TopLeftBox;
        private RectangleF TopBox;
        private RectangleF TopRightBox;
        private RectangleF LeftBox;
        private RectangleF RightBox;
        private RectangleF BottomLeftBox;
        private RectangleF BottomBox;
        private RectangleF BottomRightBox;

        private RectangleF TopBorder;
        private RectangleF LeftBorder;
        private RectangleF RightBorder;
        private RectangleF BottomBorder;

        #endregion

        #region __________ Properties

        public SizingModes SizingMode
        {
            get;
            set;
        }

        public float BorderThickness
        {
            get;
            set;
        }
        = 1F;

        public int GripSize
        {
            get;
            set;
        } = 10;

        #endregion

        #region __________ Methods

        private void RefreshBoxesAndBorders()
        {
            TopLeftBox = new RectangleF(0, 0, GripSize, GripSize);
            TopBox = new RectangleF((this.ClientSize.Width - GripSize) / 2, 0, GripSize, GripSize);
            TopRightBox = new RectangleF(this.ClientSize.Width - GripSize - BorderThickness, 0, GripSize, GripSize);
            LeftBox = new RectangleF(0, (this.ClientSize.Height - GripSize) / 2, GripSize, GripSize);
            RightBox = new RectangleF(this.ClientSize.Width - GripSize - BorderThickness, (this.ClientSize.Height - GripSize) / 2, GripSize, GripSize);
            BottomLeftBox = new RectangleF(0, this.ClientSize.Height - GripSize - BorderThickness, GripSize, GripSize);
            BottomBox = new RectangleF((this.ClientSize.Width - GripSize) / 2, this.ClientSize.Height - GripSize - BorderThickness, GripSize, GripSize);
            BottomRightBox = new RectangleF(this.ClientSize.Width - GripSize - BorderThickness, this.ClientSize.Height - GripSize - BorderThickness, GripSize, GripSize);

            TopBorder = new Rectangle(0, 0, this.ClientSize.Width, GripSize);
            LeftBorder = new Rectangle(0, 0, GripSize, this.ClientSize.Height);
            RightBorder = new Rectangle(this.ClientSize.Width - GripSize, 0, GripSize, this.ClientSize.Height);
            BottomBorder = new Rectangle(0, this.ClientSize.Height - GripSize, this.ClientSize.Width, GripSize);
        }

        #endregion

        #region __________ Event Handler

        public FloatingPanel()
        {
            this.DoubleBuffered = true;
            this.ResizeRedraw = true;
        }

        protected override void WndProc(ref Message m)
        {
            const uint WM_NCHITTEST = 0x0084;
            const uint WM_MOUSEMOVE = 0x0200;
            const uint WM_LBUTTONDOWN = 0x0201;
            const uint WM_LBUTTONUP = 0x0202;
            const uint HTLEFT = 10;
            const uint HTRIGHT = 11;
            const uint HTBOTTOMRIGHT = 17;
            const uint HTBOTTOM = 15;
            const uint HTBOTTOMLEFT = 16;
            const uint HTTOP = 12;
            const uint HTTOPLEFT = 13;
            const uint HTTOPRIGHT = 14;


            bool handled = false;

            if (m.Msg == WM_LBUTTONDOWN || m.Msg == WM_LBUTTONUP)
            {

            }

            if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
            {
                Dictionary<uint, RectangleF> boxes;
                Point screenPoint = new Point(m.LParam.ToInt32());
                Point clientPoint = this.PointToClient(screenPoint);

                RefreshBoxesAndBorders();

                switch (SizingMode)
                {
                    case SizingModes.Normal:
                        boxes = new Dictionary<uint, RectangleF>()
                        {
                            { HTBOTTOMLEFT,  BottomLeftBox },
                            { HTBOTTOMRIGHT, BottomRightBox },
                            { HTTOPRIGHT,    TopRightBox },
                            { HTTOPLEFT,     TopLeftBox },
                            { HTBOTTOM,      BottomBorder },
                            { HTRIGHT,       RightBorder },
                            { HTTOP,         TopBorder },
                            { HTLEFT,        LeftBorder }
                        };
                        break;

                    case SizingModes.GripBoxesOnly:
                        boxes = new Dictionary<uint, RectangleF>()
                        {
                            { HTBOTTOMLEFT,  BottomLeftBox },
                            { HTBOTTOM,      BottomBox },
                            { HTBOTTOMRIGHT, BottomRightBox },
                            { HTRIGHT,       RightBox },
                            { HTTOPRIGHT,    TopRightBox },
                            { HTTOP,         TopBox },
                            { HTTOPLEFT,     TopLeftBox },
                            { HTLEFT,        LeftBox }
                        };
                        break;

                    default:
                        throw new NotImplementedException();
                }

                foreach (KeyValuePair<uint, RectangleF> hitBox in boxes)
                {
                    if (hitBox.Value.Contains(clientPoint))
                    {
                        m.Result = (IntPtr)hitBox.Key;
                        handled = true;

                        break;
                    }
                }
            }

            if (!handled)
                base.WndProc(ref m);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            RefreshBoxesAndBorders();

            Pen pen = new Pen(Color.FromArgb(255, 0, 78), 2 * BorderThickness)
            {
                DashPattern = new float[] { 5, 5 }
            };

            // Draw dashed borders
            //
            e.Graphics.DrawLines(pen, new PointF[]
            {
            new PointF(BorderThickness, BorderThickness),
            new PointF(this.ClientSize.Width - BorderThickness, BorderThickness),
            new PointF(this.ClientSize.Width - BorderThickness, this.ClientSize.Height - BorderThickness),
            new PointF(BorderThickness, this.ClientSize.Height - BorderThickness),
            new PointF(BorderThickness, BorderThickness),
            });

            // Fill grip boxes
            //
            e.Graphics.FillRectangles(new SolidBrush(Color.White), new RectangleF[] { TopLeftBox, TopBox, TopRightBox, LeftBox, RightBox, BottomLeftBox, BottomBox, BottomRightBox });

            // Draw grip box borders
            //
            e.Graphics.DrawRectangles(new Pen(Color.Black, BorderThickness), new RectangleF[] { TopLeftBox, TopBox, TopRightBox, LeftBox, RightBox, BottomLeftBox, BottomBox, BottomRightBox });
        }

        #endregion
    }
}

and here is how the panel look at the design time.

Design time

The main form properties (from the designer):

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.Gray;
this.ClientSize = new System.Drawing.Size(350, 350);
this.Controls.Add(this.ScenePanel);
this.DoubleBuffered = true;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "MainForm";
this.Opacity = 0.7D;
this.Text = "MainForm";
this.TopMost = true;
this.TransparencyKey = System.Drawing.Color.Green;
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;

CodePudding user response:

Wild guess; try WM_NCLBUTTONDOWN and WM_NCLBUTTONUP instead.

If that doesn't help, try running Spy to see which mouse messages are in fact sent. Also, handling mouse messages that do not belong to your own window (i.e. the Desktop) requires a mouse hook.

  • Related