WM_NCLBUTTONUP消息在拖动表单结尾时没有发送,怎么办?

编辑: tl;博士去第一条评论。

这个问题源于另一个问题, 当鼠标在表格边框上出现时 ,我的获取MouseDown事件?

在那个问题中,当用户在表单边框上按下鼠标左键(准备拖动)时,我需要有一个表单触发事件,这非常有效。 问题是当用户完成此操作时,通过放开鼠标左键,我还想要触发一个事件。

为此,我将此代码生成为“基本表单”类,其他表单将从中派生。 我已经删除了FireMouseButton...()方法以简洁; 他们解雇自定义事件。

 const int WM_NCLBUTTONUP = 0xA2; const int WM_NCLBUTTONDWN = 0xA1; protected override void WndProc(ref Message m) { if (m.Msg == WM_NCLBUTTONUP) FireMouseButtonUp(); if (m.Msg == WM_NCLBUTTONDWN) FireMouseButtonDown(); base.WndProc(ref m); } 

这样做的问题是WM_NCLBUTTONUP消息没有像我预期的那样发送。 在查看WM_NCLBUTTONUP的描述后,我可以看到为什么,

当用户在窗口的非客户区域内释放鼠标左键时,会发布[WM_NCLBUTTONUP]。 此消息将发布到包含光标的窗口中。 如果窗口捕获了鼠标,则不会发布此消息。

由于表单在拖动时捕获了鼠标,因此不会收到WM_NCLBUTTONUP消息。 (如果forms最大化的话会这样)。 这个问题解释得更好一个窗口未最大化时丢失的WM_NCLBUTTONUP消息的奇怪问题 。

这个问题的答案有点帮助,但对我来说却引起很多困惑。 在下面的代码中我有一个小的SSCCE,它实现了从上面的答案的解决方案给出的一些代码,检查WMNCHITTEST消息以查看鼠标是否已被释放;

这个想法是当鼠标在表单中移动时应该发送WM_NCHITTEST 。 因此,一旦拖动停止,此消息应在WndProc消息参数中以鼠标位置作为DragStartPoint发送; 收到WM_NCLBUTTONDOWN消息时记录DragStartPoint位置。

但问题是WM_NCHITTEST并不总是在拖动开始后发送,只有在顶部边框的远端开始拖动时才会发送(见下图)。 单击顶部边框时始终发送WM_NCLBUTTONDOWN消息(从不对侧或底部)。 所以这很好,但WM_NCHITTEST和指出WM_NCLBUTTONUP被发送,但有时只发送。

在此处输入图像描述

如何让WM_NCHITTEST “test”在下面的代码中工作,以便在用户停止拖动表单后收到通知? (“test”正在检查WM_NCHITTEST的if语句中的DragStartPoint

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace MouseEventTest { public partial class Form1 : Form { Random rand = new Random(); public Form1() { InitializeComponent(); } const int WM_NCHITTEST = 0x84; const int WM_NCLBUTTONUP = 0xA2; const int WM_NCLBUTTONDWN = 0xA1; public Point DragStartPoint { get; set; } protected override void WndProc(ref Message m) { if (m.Msg == WM_NCLBUTTONUP) { label1.Text = "Mouse Up On Border"; } if (m.Msg == WM_NCLBUTTONDWN) { label1.Text = "Mouse Down On Border"; Point pos = lParamToPoint(m.LParam); DragStartPoint = this.PointToClient(pos); Console.Out.WriteLine("DragStartPoint: " + DragStartPoint); } if(m.Msg == WM_NCHITTEST) { Point pos = lParamToPoint(m.LParam); Console.Out.WriteLine("HtTestPnt: " + this.PointToClient(pos)); if (DragStartPoint == this.PointToClient(pos)) { label1.Text = "Mouse Up HitTest"; } } base.WndProc(ref m); } private Point lParamToPoint(IntPtr lParamIn) { int x = lParamIn.ToInt32() & 0x0000FFFF; int y = (int)((lParamIn.ToInt32() & 0xFFFF0000) >> 16); return new Point(x, y); } ///  /// Required designer variable. ///  private System.ComponentModel.IContainer components = null; ///  /// Clean up any resources being used. ///  /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code ///  /// Required method for Designer support - do not modify /// the contents of this method with the code editor. ///  private void InitializeComponent() { this.label1 = new System.Windows.Forms.Label(); this.SuspendLayout(); // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(42, 30); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(91, 13); this.label1.TabIndex = 0; this.label1.Text = "99999999999999"; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(185, 75); this.Controls.Add(this.label1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label label1; } }