在没有状态栏的情况下获得窗口的尺寸调整柄(“grippie”)






4.23/5 (17投票s)
一篇描述如何向窗口添加大小调整柄控件的文章。
引言
正如 Jensen Harris 提到的,许多应用程序为了获得一个调整大小的“grippie”而倾向于从用户那里窃取屏幕空间,仅仅是为了添加一个状态栏。
这篇文章致力于在保持馅饼完整的同时吃掉它。我将向您展示如何在不使用状态栏的情况下添加“grippie”。
创建“grippie”
第一个惊喜是,grippie 实际上是一个滚动条。好吧,至少对我来说是个惊喜。由于我喜欢使用 MFC 进行 UI 工作,因此我选择一个 MFC 对话框应用程序来演示这个技巧。首先,我将对话框的边框样式设置为“Resizing
”;否则,为什么还要费心使用“grippie”?在我的对话框中,我添加了一个类型为 CScrollBar
的新成员变量,如下所示
// GrippieTestDlg.h : header file #pragma once // CGrippieTestDlg dialog class CGrippieTestDlg : public CDialog { public: CGrippieTestDlg(CWnd* pParent = NULL); enum { IDD = IDD_GRIPPIETEST_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); protected: HICON m_hIcon; virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: afx_msg void OnBnClickedOk(); private: CScrollBar m_grippie; };
现在,我为对话框的 WM_CREATE
消息添加一个处理程序,MFC 向导在我的对话框类中为我提供了处理程序 OnCreate(LPCREATESTRUCT lpCreateStruct)
。我在处理程序中添加以下代码来创建我的“grippie”
#define GRIPPIE_SQUARE_SIZE 11 int CGrippieTestDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDialog::OnCreate(lpCreateStruct) == -1) return -1; CRect clientRect; GetClientRect(&clientRect); CRect grippieRect; grippieRect.right=clientRect.right; grippieRect.bottom=clientRect.bottom; grippieRect.left=clientRect.right-GRIPPIE_SQUARE_SIZE; grippieRect.top=clientRect.bottom-GRIPPIE_SQUARE_SIZE; m_grippie.Create(WS_CHILD|WS_VISIBLE|SBS_SIZEGRIP|WS_CLIPSIBLINGS, grippieRect, this, 0); return 0; }
我们这里有一个不错的“grippie”,并且运行良好。但是等等!当您调整窗口大小时,这该死的东西停留在同一个地方。好吧,没问题,我们将修复它。
在窗口大小调整时移动 grippie
我们将通过为对话框的 WM_SIZE
事件添加处理程序来修复它
void CGrippieTestDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); CRect clientRect; GetClientRect(&clientRect); if(m_grippie.m_hWnd!=NULL) m_grippie.SetWindowPos(NULL, clientRect.right-GRIPPIE_SQUARE_SIZE, clientRect.bottom-GRIPPIE_SQUARE_SIZE, GRIPPIE_SQUARE_SIZE, GRIPPIE_SQUARE_SIZE, SWP_NOZORDER|SWP_SHOWWINDOW); }
现在,让我们把它包装在一个漂亮的类中。
CGrippie 类
我将在我的项目中添加一个类,该类派生自 CScrollBar
并称为 CGrippie
// Grippie.h : header file #pragma once #define GRIPPIE_SQUARE_SIZE 11 class CGrippie : public CScrollBar { DECLARE_DYNAMIC(CGrippie) CGrippie(const CGrippie& other); void operator=(const CGrippie& other); public: CGrippie(); virtual ~CGrippie(); protected: DECLARE_MESSAGE_MAP() public: BOOL Create(CWnd* parent); void OnParentSize(void); private: CWnd* m_parent; };
这是实现
// Grippie.cpp : implementation file #include "stdafx.h" #include "GrippieTest.h" #include "Grippie.h" IMPLEMENT_DYNAMIC(CGrippie, CScrollBar) CGrippie::CGrippie() { m_parent=NULL; } CGrippie::~CGrippie() { } BEGIN_MESSAGE_MAP(CGrippie, CScrollBar) END_MESSAGE_MAP() BOOL CGrippie::Create(CWnd* parent) { m_parent=parent; if(m_parent==NULL) return FALSE; CRect clientRect; m_parent->GetClientRect(&clientRect); CRect grippieRect; grippieRect.right=clientRect.right; grippieRect.bottom=clientRect.bottom; grippieRect.left=clientRect.right-GRIPPIE_SQUARE_SIZE; grippieRect.top=clientRect.bottom-GRIPPIE_SQUARE_SIZE; return CScrollBar::Create(WS_CHILD|WS_VISIBLE|SBS_SIZEGRIP, grippieRect, m_parent, 0); } void CGrippie::OnParentSize(void) { if(m_parent==NULL) return; CRect clientRect; m_parent->GetClientRect(&clientRect); if(m_hWnd!=NULL) SetWindowPos(NULL, clientRect.right-GRIPPIE_SQUARE_SIZE, clientRect.bottom-GRIPPIE_SQUARE_SIZE, GRIPPIE_SQUARE_SIZE, GRIPPIE_SQUARE_SIZE, SWP_NOZORDER|SWP_SHOWWINDOW); }
现在,我们所需要做的就是将对话框类中的 m_grippie
变量的类型更改为 CGrippie
,并像这样更改对话框消息处理程序的实现
int CGrippieTestDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDialog::OnCreate(lpCreateStruct) == -1) return -1; m_grippie.Create(this); return 0; } void CGrippieTestDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); m_grippie.OnParentSize(); }
好吧,如果您使用的是 VS2005,那么您现在就完成了,但对于 VS2003 代码,仍然需要处理一件小事:即当鼠标指针位于 grippie 区域中,但不在对话框的右下角时,鼠标指针的形状。形状应该是这样的: 。但实际上,它是一个常规鼠标指针。
在 VS2003 中处理鼠标指针
为了解决这个问题,我将为 WM_SETCURSOR
消息添加一个处理程序。为此,我将向消息映射添加一个事件
BEGIN_MESSAGE_MAP(CGrippie, CScrollBar) ON_WM_SETCURSOR() END_MESSAGE_MAP()
使用以下代码覆盖 CGrippie
中的父方法 OnSetCursor
BOOL CGrippie::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { if ( pWnd == this && nHitTest == HTCLIENT ) { ::SetCursor( ::LoadCursor( NULL, IDC_SIZENWSE ) ); return( TRUE ); } return CScrollBar::OnSetCursor(pWnd, nHitTest, message); }
好吧,就是这样!我们在对话框中有一个 grippie,而没有那个状态栏!