Сообщение 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;
    }
}

Ответы на вопрос(0)

Ваш ответ на вопрос