创建一个“快照”使用WndProc, c#对窗体进行定位
本文关键字:WndProc 窗体 定位 使用 一个 快照 创建 | 更新日期: 2023-09-27 17:50:21
我正试图在c#中为WinForm应用程序添加"snap to grid"功能,但我在使表单正确移动方面遇到了一点问题。
期望的结果是用户单击并拖动窗体,窗体的位置沿着鼠标所需的方向移动,但以50像素的增量移动,并且始终向下四舍五入到最后一个网格点。当单击并拖动边框时,窗体的大小将以相同的50像素增量进行调整。
我已经能够通过计算WM_SIZING消息中的新大小来正确地调整大小。我试图在WM_MOVING中做同样的事情,但我没有得到正确的功能。
我得到的是,表单将不会移动到任何网格点更高(右或下)比它的开始位置,当鼠标移动到网格点较低(上或左)1像素时,表单跳50像素。当然,这是我们想要的移动到较低的点,但它应该只移动一次,然后等待鼠标移动额外的空间,然后再进行下一次跳跃,然而,它在鼠标移动下一个1像素时移动了50像素。
我发现的是窗口在消息中的位置。LParam在每条消息中计算,仅在WM_MOVE完成时更新。即使没有释放鼠标按钮也是如此。因此,移动鼠标的顶部或左侧强制窗口移动50像素和下一个消息(即:鼠标移动的另一个1像素),LParam现在是50像素低,1像素足以捕捉到下一个较低的网格点。然而,因为我不想让窗口向右或向下移动,直到鼠标从第一次点击点移动了50个像素,windowproc使窗口位置保持不变,它永远不会达到比开始位置更高的点。由于某些原因,WM_SIZING不能这样做。即使屏幕上看不到窗口的大小变化,它也会看到窗口的大小在变化。
这是我使用的代码。为了清楚起见,WM_SIZING部分已被删除。
private const int WM_MOVING = 0x216;
private const int WM_MOVE = 0x3;
struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOVING)
{
RECT rc = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
int w = rc.Right - rc.Left;
int h = rc.Bottom - rc.Top;
rc.Top = ((int)rc.Top / 50) * 50;
rc.Left = ((int)rc.Left / 50) * 50;
rc.Right = rc.Left + w;
rc.Bottom = rc.Top + h;
Marshal.StructureToPtr(rc, m.LParam, true);
}
base.WndProc(ref m);
}
感谢您对这个问题的任何帮助或见解。达斯汀。
对于每个WM_MOVING消息,由于鼠标自最后一个WM_MOVING消息以来的相对运动,窗口坐标与前一个窗口坐标偏移。例如,如果窗口的左坐标最初为100,并且鼠标在X方向上从130移动到131,则您将收到一个左坐标为101的矩形,该矩形在处理程序中被快速恢复为100。现在假设鼠标在X中从131移动到132:您可能期望新的左坐标为102(因为从您开始移动窗口以来,鼠标在X中总共移动了2个像素),但实际上它将是101(100加上鼠标自上次事件以来的1个像素的相对移动)。
因此,您需要在单个动作中将鼠标向右轻击50像素,然后才能使窗口移动到下一个快照位置。另一方面,你只需要每次向左移动1像素,就可以捕捉到下一个最低的50像素边界。
要解决这个问题,您需要跟踪被捕获的窗口位置和没有被捕获的窗口位置之间的累积偏移量。下面的代码似乎完成了您想要实现的。
(另一个修改是在确定snap位置时使用舍入而不是截断。这将使拖动时鼠标光标与标题栏之间的最大可能距离最小化。
private const int WM_MOVING = 0x216;
private const int WM_EXITSIZEMOVE = 0x231;
struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
private int LeftOffset = 0;
private int TopOffset = 0;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOVING)
{
RECT rc = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
int w = rc.Right - rc.Left;
int h = rc.Bottom - rc.Top;
int newTop = (int)Math.Round((rc.Top + TopOffset) / 50.0) * 50;
int newLeft = (int)Math.Round((rc.Left + LeftOffset) / 50.0) * 50;
TopOffset = rc.Top + TopOffset - newTop;
LeftOffset = rc.Left + LeftOffset - newLeft;
rc.Top = newTop;
rc.Left = newLeft;
rc.Right = newLeft + w;
rc.Bottom = newTop + h;
Marshal.StructureToPtr(rc, m.LParam, true);
}
else if (m.Msg == WM_EXITSIZEMOVE)
{
LeftOffset = 0;
TopOffset = 0;
}
base.WndProc(ref m);
}