吸附窗口
使窗口吸附到屏幕边缘的简单类

引言
本文介绍了一个简单的类,可以使窗口吸附到桌面工作区域的边缘,就像流行的 Winamp 程序一样。工作区域是指未被系统任务栏或应用程序桌面工具栏遮挡的屏幕部分(如 MSDN 中所述)。要临时禁用在拖动窗口时的吸附,请按住吸附修改键(默认情况下为 Shift 键)。
本文重用了 Peter Hesselberg 的文章 Make it snappy 中的代码,因此建议先阅读它。 在 CodeProject 上还有类似的的文章,WTL 吸附分割窗口 和 可自动隐藏的吸附工具窗口,但它们处理的是特定问题,而本文则提供了一种通用的方法。
代码
代码使用纯 Win32 API。下载包包括 Dev-C++ (GCC) 和 Win32/WTL VC2005 项目(使用 VC2005 Express + Platform SDK 2003 R2 + WTL 8.0 构建)。
吸附距离和吸附修改键存储在 snap_Margin
和 snap_ModifierKey
类的公共成员中。 默认情况下,吸附距离设置为窗口标题栏的高度,无吸附键为 Shift 键。 工作区域大小在 WM_ENTERSIZEMOVE 处理程序中检测,以便桌面大小更改时窗口可以正确吸附。 函数声明为 virtual
,因此您可以轻松地进行自己的窗口位置处理。
在 WTL 中使用代码
遵循通常的 WTL 实践,代码扩展为模板类。 将 CSnapWindow
添加到 CMainFrame
继承列表中并链接消息映射
//WTL example
#include "../Snapwindow.h"
class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>,
public CMessageFilter, public CIdleHandler, public CSnapWindow<CMainFrame>
...
BEGIN_MSG_MAP(CMainFrame)
...
CHAIN_MSG_MAP(CSnapWindow<CMainFrame>)
END_MSG_MAP()
在 Win32 中使用代码
创建一个类实例,并将类成员作为主窗口的消息处理程序添加
//Dev-C++ (GCC) example:
#include "SnapWindow.h"
CSnapWindow snapHandler;
...
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
/////////////////////////////////////////////
//snap handling
//
case WM_MOVING:
return snapHandler.OnSnapMoving(hwnd, message, wParam, lParam);
break;
case WM_ENTERSIZEMOVE:
return snapHandler.OnSnapEnterSizeMove(hwnd, message, wParam, lParam);
break;
//
/////////////////////////////////////////////
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
源代码
类的完整源代码在此处提供,以便您可以立即开始使用它
#ifndef _0C46500C_084D_44ac_8C26_37E38BED2714_
#define _0C46500C_084D_44ac_8C26_37E38BED2714_
#pragma once
#ifdef __ATLBASE_H__
template <class T> class CSnapWindow
#else
class CSnapWindow
#endif
{
public:
int snap_Margin, snap_ModifierKey;
#ifdef __ATLBASE_H__
BEGIN_MSG_MAP(CSnapWindow<T>)
MESSAGE_HANDLER(WM_MOVING, OnSnapMoving)
MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnSnapEnterSizeMove)
END_MSG_MAP()
#endif
#ifdef __ATLBASE_H__
virtual LRESULT OnSnapEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
#else
virtual LRESULT OnSnapEnterSizeMove(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam)
#endif
{
snap_cur_pos.x=0;
snap_cur_pos.y=0;
snap_rcWindow.bottom=0;
snap_rcWindow.left=0;
snap_rcWindow.right=0;
snap_rcWindow.top=0;
#ifdef __ATLBASE_H__
T* pT = static_cast<T*>(this);
GetWindowRect(pT->m_hWnd, &snap_rcWindow );
#else
GetWindowRect(hWnd, &snap_rcWindow );
#endif
GetCursorPos( &snap_cur_pos );
snap_x = snap_cur_pos.x - snap_rcWindow.left;
snap_y = snap_cur_pos.y - snap_rcWindow.top;
return 0;
}
#ifdef __ATLBASE_H__
virtual LRESULT OnSnapMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
#else
virtual LRESULT OnSnapMoving(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
#endif
{
//no snap if modifier key pressed
if (GetAsyncKeyState(snap_ModifierKey) < 0) return FALSE;
snap_prc = (LPRECT)lParam;
snap_cur_pos.x=0;
snap_cur_pos.y=0;
snap_rcWindow.bottom=0;
snap_rcWindow.left=0;
snap_rcWindow.right=0;
snap_rcWindow.top=0;
GetCursorPos( &snap_cur_pos );
OffsetRect( snap_prc,
snap_cur_pos.x - (snap_prc->left + snap_x) ,
snap_cur_pos.y - (snap_prc->top + snap_y) );
//working area may change during app lifetime
SystemParametersInfo( SPI_GETWORKAREA, 0, &snap_wa, 0 );
if (isSnapClose( snap_prc->left, snap_wa.left ))
{OffsetRect( snap_prc, snap_wa.left - snap_prc->left, 0);}
else
if (isSnapClose( snap_wa.right, snap_prc->right ))
{OffsetRect( snap_prc, snap_wa.right - snap_prc->right, 0);}
if (isSnapClose( snap_prc->top, snap_wa.top ))
{OffsetRect( snap_prc, 0, snap_wa.top - snap_prc->top );}
else
if (isSnapClose( snap_wa.bottom, snap_prc->bottom ))
{OffsetRect( snap_prc, 0, snap_wa.bottom - snap_prc->bottom );}
return TRUE;
}
virtual BOOL isSnapClose( int a, int b ) { return (abs( a - b ) < snap_Margin);}
CSnapWindow()
{
snap_ModifierKey=VK_SHIFT;
NONCLIENTMETRICS ncm = { 0 };
ncm.cbSize = sizeof ncm;
SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
snap_Margin=ncm.iCaptionHeight;
}
private:
POINT snap_cur_pos;
RECT snap_rcWindow, snap_wa, *snap_prc;
int snap_x, snap_y;
};
#endif//_0C46500C_084D_44ac_8C26_37E38BED2714_
问题/建议
请在此文章的留言板中发布任何问题/错误/建议。