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);
}
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.
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.