I am implementing a screenshot in my application, with the user being able to select which part will be captured, for this purpose, i created a control to overlay a Picturebox using the example provided in this Link: Creating Custom Picturebox with Draggable and Resizable Selection Window
The control works well, doing what needs to be done, but what I'm having difficulty implementing is that when the user clicks on a button outside the control, it would no longer be moved, changing its cursor from SizeAll to Arrow, but could still be resizable.
How can I perform such a task?
This is the code I'm using now to create my control
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ControlSizable
{
public partial class FrameControl: UserControl
{
public FrameControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
DoubleBuffered = true;
ResizeRedraw = true;
BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (var p = new Pen(Color.White , 3))
{
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
e.Graphics.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
}
}
const int WM_NCHITTEST = 0x84;
const int WM_SETCURSOR = 0x20;
const int WM_NCLBUTTONDBLCLK = 0xA3;
protected override void WndProc(ref Message m)
{
int borderWidth = 10;
if (m.Msg == WM_SETCURSOR) /*Setting cursor to SizeAll*/
{
if ((m.LParam.ToInt32() & 0xffff) == 0x2 /*Move*/)
{
Cursor.Current = Cursors.SizeAll;
m.Result = (IntPtr)1;
return;
}
}
if ((m.Msg == WM_NCLBUTTONDBLCLK)) /*Disable Mazimiz on Double click*/
{
m.Result = (IntPtr)1;
return;
}
base.WndProc(ref m);
if (m.Msg == WM_NCHITTEST)
{
var pos = PointToClient(new Point(m.LParam.ToInt32() & 0xffff,
m.LParam.ToInt32() >> 16));
if (pos.X <= ClientRectangle.Left + borderWidth &&
pos.Y <= ClientRectangle.Top + borderWidth)
m.Result = new IntPtr(13); //TOPLEFT
else if (pos.X >= ClientRectangle.Right - borderWidth &&
pos.Y <= ClientRectangle.Top + borderWidth)
m.Result = new IntPtr(14); //TOPRIGHT
else if (pos.X <= ClientRectangle.Left + borderWidth &&
pos.Y >= ClientRectangle.Bottom - borderWidth)
m.Result = new IntPtr(16); //BOTTOMLEFT
else if (pos.X >= ClientRectangle.Right - borderWidth &&
pos.Y >= ClientRectangle.Bottom - borderWidth)
m.Result = new IntPtr(17); //BOTTOMRIGHT
else if (pos.X <= ClientRectangle.Left + borderWidth)
m.Result = new IntPtr(10); //LEFT
else if (pos.Y <= ClientRectangle.Top + borderWidth)
m.Result = new IntPtr(12); //TOP
else if (pos.X >= ClientRectangle.Right - borderWidth)
m.Result = new IntPtr(11); //RIGHT
else if (pos.Y >= ClientRectangle.Bottom - borderWidth)
m.Result = new IntPtr(15); //Bottom
else
m.Result = new IntPtr(2); //Move
}
}
}
}
Example of a screenshot using the control:
What I'm trying to do is that when the user clicks on the arrow button, the control is no longer moved around the screen, just resized by the edges and I can draw the arrow in the drawing itself (This process of drawing the arrow, I can already do it)
Add a public bool
Property to the Custom Control, here named CanMove
, defaults to true
.
Then, in the WndProc override, when the message is WM_SETCURSOR
, you evaluate CanMove
: if it's the default value (true
), you set the Cursor to Cursors.SizeAll
, otherwise to Cursors.Arrow
(or whatever you see fit)
When the message is WM_NCHITTEST
, in the last if
condition, if CanMove is true
you return (IntPtr)2
, otherwise IntPtr.Zero
.
Add using System.ComponentModel;
on top of the FrameControl class file.
The Control is still resizable when CanMove
is false
.
So, when you click that Button, just set the CanMove
Property of the Framecontrol to false
.
using System.ComponentModel;
public class FrameControl : Control {
public FrameControl() { // [...]}
[DefaultValue(true)]
public bool CanMove { get; set; } = true;
// [...]
protected override void WndProc(ref Message m) {
if (m.Msg == WM_SETCURSOR) /*Setting cursor to SizeAll or Arrow */
{
if ((m.LParam.ToInt32() & 0xffff) == 0x2 /*Move*/) {
Cursor.Current = CanMove ? Cursors.SizeAll : Cursors.Arrow;
m.Result = (IntPtr)1;
return;
}
}
// [...]
base.WndProc(ref m);
if (m.Msg == WM_NCHITTEST) {
// [...]
if (pos.X <= ClientRectangle.Left + borderWidth &&
pos.Y <= ClientRectangle.Top + borderWidth)
m.Result = (IntPtr)13; //TOPLEFT
// [...]
else
m.Result = CanMove ? (IntPtr)2 : IntPtr.Zero;
}
}
}