Сообщение WM_NCLBUTTONUP не отправлено в конце перетаскивания формы, как это сделать?
РЕДАКТИРОВАТЬ: tl; dr перейти к первому комментарию.
Этот вопрос проистекает из другого моего вопросаПолучить событие 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
сообщение. (Это будет, если форма развернута, хотя). Этот вопрос объясняет это немного лучшеЛюбопытная проблема отсутствующего сообщения WM_NCLBUTTONUP, когда окно не развернуто.
Ответ на этот вопрос несколько полезен, но вызывает у меня много путаницы. В приведенном ниже коде у меня есть небольшой SSCCE, он реализует некоторый код, полученный из решения к ответу выше, проверяяWMNCHITTEST
сообщение, чтобы увидеть, была ли мышь отпущена;
Идея в том, чтоWM_NCHITTEST
следует отправлять, когда мышь перемещается внутри формы. Таким образом, после прекращения перетаскивания это сообщение должно быть отправлено с позицией мыши какDragStartPoint
в аргументах сообщения WndProc; гдеDragStartPoint
записывается, когдаWM_NCLBUTTONDOWN
сообщение получено.
Проблема в том, что этоWM_NCHITTEST
не всегда отправляется после начала перетаскивания, только когда перетаскивание начинается на дальних сторонах верхней границы (см. рис. ниже).WM_NCLBUTTONDOWN
сообщение всегда отправляется при нажатии на верхнюю границу (ни для сторон, ни для дна). Так что это хорошо, ноWM_NCHITTEST
и как указаноWM_NCLBUTTONUP
отправлены, но только иногда.
Как я могу получитьWM_NCHITTEST
"test" для работы в приведенном ниже коде, чтобы я мог получать уведомления, как только пользователь перестал перетаскивать форму? («тест» проверяетDragStartPoint
в операторе if дляWM_NCHITTEST
)
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);
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}