确定何时移动 WPF 窗口

Posted

技术标签:

【中文标题】确定何时移动 WPF 窗口【英文标题】:Determining when a WPF window is being moved 【发布时间】:2013-09-04 08:08:12 【问题描述】:

我正在开发一个派生自 WPF Window 类的类,该类的行为类似于名为 AppBarWindow 的应用程序工具栏窗口。我已经能够找到各种 WinForms 实现,但没有 WPF 实现。

我已经编写了很多代码,但我需要知道用户何时开始在屏幕上拖动窗口以及何时停止,因为窗口的行为会有所不同。默认的 WPF 处理不太正确,所以我实现了自己的窗口过程并使用HwndSource 对象安装它。

我在一个没有非客户区的应用程序中工作。在这种情况下,有一个 LeftMouseButtonDown 事件处理程序将标志设置为 true,然后调用 DragMove 方法来拖动窗口。当该方法返回时,我将标志设置为 false。一切正常。

但我现在正在研究一个不会使用DragMove 方法的通用类。我可以为窗口添加另一个 LeftMouseButtonDown 处理程序,但我不相信如果鼠标在非客户区,它会被调用。

在这种情况下,我将如何检测用户正在拖动窗口以及何时停止?

【问题讨论】:

【参考方案1】:

通过在拖动窗口时监视从 Win32 发送到我的窗口的消息,我找到了我需要知道的内容。

简而言之,当窗口开始移动时,Windows 会发送以下消息:

WM_ENTERSIZEMOVE

接下来,Windows 依次向我的窗口过程发送以下消息:

WM_MOVING WM_WINDOWPOSCHANGING WM_GETMINMAXINFO WM_WINDOWPOSCHANGED WM_MOVE

这些后面是代码为 0xc310 的消息。这在任何地方都没有记录,所以我猜这是 .NET / WPF 在内部使用的。

这 6 条消息在鼠标移动时重复发送,窗口跟随在它后面。

最后,当你松开鼠标左键时,Windows 会发送:

WM_EXITSIZEMOVE

所以我需要监听 WM_ENTERSIZEMOVE 和 WM_EXITSIZEMOVE 消息。

【讨论】:

【参考方案2】:

在这种情况下,Window.LocationChanged 事件不会为您提供帮助。

http://msdn.microsoft.com/en-us/library/system.windows.window.locationchanged.aspx

【讨论】:

不,不会。正如我在问题中所说,“默认的 WPF 处理不太正确,所以我实现了自己的窗口过程。”我需要在那个级别进行处理。 Window.LocationChanged 在这个过程中为时已晚。我需要在 WPF 收到消息之前对其进行处理。 当包含的 WPF 窗口被移动时,我需要这个事件来关闭一个弹出控件。该活动对我有用,因为我可以看到弹出窗口及时关闭。【参考方案3】:

正如 Tony 指出的那样,窗口拖动涉及一些窗口消息。这是一个可能有帮助的枚举:

internal enum WindowsMessage

    /// <summary>Sent after a window has been moved.</summary>
    WM_MOVE = 0x0003,
    /// <summary>
    /// Sent to a window when the size or position of the window is about to change.
    /// An application can use this message to override the window's default maximized size and position,
    /// or its default minimum or maximum tracking size.
    /// </summary>
    WM_GETMINMAXINFO = 0x0024,
    /// <summary>
    /// Sent to a window whose size, position, or place in the Z order is about to change as a result
    /// of a call to the SetWindowPos function or another window-management function.
    /// </summary>
    WM_WINDOWPOSCHANGING = 0x0046,
    /// <summary>
    /// Sent to a window whose size, position, or place in the Z order has changed as a result of a
    /// call to the SetWindowPos function or another window-management function.
    /// </summary>
    WM_WINDOWPOSCHANGED = 0x0047,
    /// <summary>
    /// Sent to a window that the user is moving. By processing this message, an application can monitor
    /// the position of the drag rectangle and, if needed, change its position.
    /// </summary>
    WM_MOVING = 0x0216,
    /// <summary>
    /// Sent once to a window after it enters the moving or sizing modal loop. The window enters the
    /// moving or sizing modal loop when the user clicks the window's title bar or sizing border, or
    /// when the window passes the WM_SYSCOMMAND message to the DefWindowProc function and the wParam
    /// parameter of the message specifies the SC_MOVE or SC_SIZE value. The operation is complete
    /// when DefWindowProc returns.
    /// <para />
    /// The system sends the WM_ENTERSIZEMOVE message regardless of whether the dragging of full windows
    /// is enabled.
    /// </summary>
    WM_ENTERSIZEMOVE = 0x0231,
    /// <summary>
    /// Sent once to a window once it has exited moving or sizing modal loop. The window enters the
    /// moving or sizing modal loop when the user clicks the window's title bar or sizing border, or
    /// when the window passes the WM_SYSCOMMAND message to the DefWindowProc function and the
    /// wParam parameter of the message specifies the SC_MOVE or SC_SIZE value. The operation is
    /// complete when DefWindowProc returns.
    /// </summary>
    WM_EXITSIZEMOVE = 0x0232

【讨论】:

以上是关于确定何时移动 WPF 窗口的主要内容,如果未能解决你的问题,请参考以下文章

WPF 动画窗口可见性更改

子窗口可以知道何时移动父窗口吗?

如何在 WPF 中从一个窗口移动到另一个窗口?

WPF编程怎么激活指定窗口?

如何通过拖动扩展的窗口框架使 WPF 窗口可移动?

当 WPF 弹出窗口的锚元素移动时,如何移动它?