Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by MeteorTracker128

Why Are Only the Resize Events Triggering in My Custom PictureBox User Control?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I used the accepted answer by @RezaAghaei from

Creating Custom Picturebox with Draggable and Resizable Selection Window

and it works perfectly. However, only the Resize event is firing, while events such as Click, DoubleClick, Enter, and MouseEnter are not being triggered. I need to add multiple user controls to the PictureBox so that when a user clicks on any of them, it displays properties like name, image path, size, and more.

I’ve tried several approaches without success. Below is the full code for my custom control:

CSHARP
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; using System.Text; using System.Threading.Tasks; using Microsoft.VisualBasic; public class userControlAddImage : UserControl { // Constructor to set some default properties public userControlAddImage() { this.BackgroundImage = Image.FromFile(@"D:\UserImportant\Desktop\New folder (8)\29927.jpg"); this.BackgroundImageLayout = ImageLayout.Zoom; this.BorderStyle = BorderStyle.FixedSingle; } private void userControlAddImage_Click(object sender, EventArgs e) { MessageBox.Show("User Control clicked!"); } private void userControlAddImage_DoubleClick(object sender, EventArgs e) { MessageBox.Show("User Control userControlAddImage_DoubleClick!"); } private void userControlAddImage_Enter(object sender, EventArgs e) { MessageBox.Show("User Control userControlAddImage_Enter!"); } private void userControlAddImage_MouseEnter(object sender, EventArgs e) { MessageBox.Show("User Control userControlAddImage_MouseEnter!"); } private void userControlAddImage_Resize(object sender, EventArgs e) { MessageBox.Show("User Control userControlAddImage_Resize!"); } const int WM_NCHITTEST = 0x84; const int WM_SETCURSOR = 0x20; const int WM_NCLBUTTONDBLCLK = 0xA3; protected override void WndProc(ref Message m) { var borderWidth = 10; if (m.Msg == WM_SETCURSOR) { if ((m.LParam.ToInt32() & 0xFFFF) == 0x2) { Cursor.Current = Cursors.SizeAll; m.Result = (IntPtr)1; return; } } if (m.Msg == WM_NCLBUTTONDBLCLK) { m.Result = (IntPtr)1; return; } base.WndProc(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 } } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle = cp.ExStyle | 0x2000000; // Turn on WS_EX_COMPOSITED return cp; } } }

I would appreciate any guidance or solutions to ensure that all events are triggered as expected.

1

Comments 0

Answers 2

Views 103

2 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

It turns out that the way your custom control handles hit testing is causing the problem. In your overridden WndProc, when you process the WM_NCHITTEST message, you’re returning HTCAPTION (which has the value 2) when none of your border conditions match. This tells Windows that the hit occurred in a non‑client area (essentially the title bar) rather than in the client area. Consequently, standard client area mouse events (like Click, DoubleClick, etc.) are not raised.

To fix this, change the default hit‐test return value from HTCAPTION (2) to HTCLIENT (1) so that the system correctly treats the area as part of the client. For example, modify your WM_NCHITTEST handling code like this:

CSHARP
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(1); // HTCLIENT instead of 2 (HTCAPTION) return; }

By returning HTCLIENT (1) for the default region, the Windows message loop will handle clicks and other mouse events normally, and your user control’s events should start firing as expected.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by QuasarObserver210 1 month ago

0

A modified version of the original User Control.

What has been changed:

  • When WM_NCHITTEST is received and the Mouse pointer is in the Client Area of the window, m.Result is set to HTCLIENT (= 1). The original code was turning this value into HTCAPTION (= 2), to trigger the default functionality of the Mouse when the left Button is down: drag the window. This can be handy in some cases, but other events are not generated anymore when the Mouse pointer is, allegedly, over the Caption of a window. For example, the Click event.

    The code here restores the default behavior, the HTCLIENT location is preserved, the UserControl movement is triggered and calculated using the OnMouseDown and OnMouseMove methods, so all other events are working again

  • WM_SETCURSOR is not handled anymore, the Cursor shape is changed when the Mouse is moved while keeping the Left Button pressed

  • The UserControl was missing the InitializeComponent() call

  • You don't usually subscribe to events of your own derived class, you override the methods that raise the events: e.g., do not subscribe to the Click event, override OnClick etc.

  • Do not, ever, load the image from a path in a custom component, especially using an absolute path: when you deploy your component, that path will not exist. While in design mode, the source path of the Visual Studio designer can (will) be something entirely different, so relative paths won't work either (or, are bound to fail, causing an exception in the Designer). Use an Image stored as embedded resource instead.

  • Bonus: naming conventions in C# require that the name of a class start with a capital letter. It may not seem that important, but a C# dev that looks at your code may (will) immediately become skeptical, can't avoid thinking that you, quite probably, don't know what you're doing (or you're coming from somewhere else; same thing, different mindset). You may want to avoid this


CSHARP
public class UserControlAddImage : UserControl { Point mouseDownPosition = Point.Empty; public UserControlAddImage() { InitializeComponent(); BackgroundImage = Properties.Resources.[SomeImageFromResources]; BackgroundImageLayout = ImageLayout.Zoom; BorderStyle = BorderStyle.FixedSingle; } private void InitializeComponent() { SuspendLayout(); Size = new Size(150, 150); ResumeLayout(false); } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle = cp.ExStyle | 0x2000000; // WS_EX_COMPOSITED return cp; } } protected override void WndProc(ref Message m) { base.WndProc(ref m); const int borderWidth = 10; if (m.Msg == 0x84) { // WM_NCHITTEST var pos = PointToClient(new Point(m.LParam.ToInt32())); 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 } } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (e.Button != MouseButtons.Left) return; mouseDownPosition = e.Location; } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (e.Button != MouseButtons.Left) return; Cursor.Current = Cursors.SizeAll; Location = new Point(Location.X + (e.Location.X - mouseDownPosition.X), Location.Y + (e.Location.Y - mouseDownPosition.Y)); } protected override void OnMouseUp(MouseEventArgs e) { Cursor.Current = Cursors.Default; base.OnMouseUp(e); } protected override void OnClick(EventArgs e) { base.OnClick(e); Console.WriteLine("User Control clicked!"); } protected override void OnDoubleClick(EventArgs e) { base.OnDoubleClick(e); Console.WriteLine("User Control DoubleClick!"); } protected override void OnEnter(EventArgs e) { base.OnEnter(e); Console.WriteLine("User Control Enter!"); } protected override void OnMouseEnter(EventArgs e) { base.OnMouseEnter(e); Console.WriteLine("User Control MouseEnter!"); } protected override void OnResize(EventArgs e) { base.OnResize(e); Console.WriteLine("User Control Resize!"); } }

No comments yet.

Discussion

No comments yet.