MFC状态栏之RepositionBars

CStatusBar、 CToolBar、 CDialogBar、 CDockBar这几个类都继承自CControlBar,

而RepositionBars() 可以用来计算CControlBar的布局(位置和大小)。

void RepositionBars(UINT nIDFirst,
    UINT nIDLast,
    UINT nIDLeftOver,
    UINT nFlag = reposDefault,
    LPRECT lpRectParam = NULL,
    LPCRECT lpRectClient = NULL,
    BOOL bStretch = TRUE) ;

具体每个参数的含义可以去查MSDN,这里主要说明这个函数是怎么起作用的。

MSDN给的解释很简单,就是说调用RepositionBars会给窗口内的所有CControlBar发送一条WM_SIZEPARENT 消息,再具体的说明就没了。

不多废话,直接上代码,下面是为了便于理解删减过的代码:

void CWnd::RepositionBars(UINT nIDFirst, UINT nIDLast, UINT nIDLeftOver,
	UINT nFlags, LPRECT lpRectParam, LPCRECT lpRectClient, BOOL bStretch)
{
	AFX_SIZEPARENTPARAMS layout;
	HWND hWndLeftOver = NULL;

    //获取客户区矩形
	GetClientRect(&layout.rect);    // starting rect comes from client rect

    //给每个顶级子窗口发送 WM_SIZEPARENT 消息
	for (HWND hWndChild = ::GetTopWindow(m_hWnd); hWndChild != NULL;
		hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
	{
		UINT_PTR nIDC = _AfxGetDlgCtrlID(hWndChild);
		CWnd* pWnd = CWnd::FromHandlePermanent(hWndChild);
		if (nIDC == nIDLeftOver)
			hWndLeftOver = hWndChild;
		else if (nIDC >= nIDFirst && nIDC <= nIDLast && pWnd != NULL)
			::SendMessage(hWndChild, WM_SIZEPARENT, 0, (LPARAM)&layout);
	}
}

可以看到MSDN指出的发送 WM_SIZEPARENT 消息,而且这个消息是发送给所有顶级子窗口的,那问题来了:假如同时存在工具栏(CToolBar)和状态栏(CStatusBar),那怎么让这2个控件分别计算自身的坐标?

继续看代码,看控件窗口收到 WM_SIZEPARENT 消息是怎么处理的:

LRESULT CControlBar::OnSizeParent(WPARAM, LPARAM lParam)
{
	AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam;
	DWORD dwStyle = RecalcDelayShow(lpLayout);

	if ((dwStyle & WS_VISIBLE) && (dwStyle & CBRS_ALIGN_ANY) != 0)
	{
		// align the control bar
		CRect rect;
		rect.CopyRect(&lpLayout->rect);

		CSize sizeAvail = rect.Size();  // maximum size available

		// get maximum requested size
		DWORD dwMode = lpLayout->bStretch ? LM_STRETCH : 0;
		if ((m_dwStyle & CBRS_SIZE_DYNAMIC) && m_dwStyle & CBRS_FLOATING)
			dwMode |= LM_HORZ | LM_MRUWIDTH;
		else if (dwStyle & CBRS_ORIENT_HORZ)
			dwMode |= LM_HORZ | LM_HORZDOCK;
		else
			dwMode |=  LM_VERTDOCK;

		CSize size = CalcDynamicLayout(-1, dwMode);

		size.cx = min(size.cx, sizeAvail.cx);
		size.cy = min(size.cy, sizeAvail.cy);

		if (dwStyle & CBRS_ORIENT_HORZ)
		{
			lpLayout->sizeTotal.cy += size.cy;
			lpLayout->sizeTotal.cx = max(lpLayout->sizeTotal.cx, size.cx);
			if (dwStyle & CBRS_ALIGN_TOP)
				lpLayout->rect.top += size.cy;
			else if (dwStyle & CBRS_ALIGN_BOTTOM)
			{
				rect.top = rect.bottom - size.cy;
				lpLayout->rect.bottom -= size.cy;
			}
		}
		else if (dwStyle & CBRS_ORIENT_VERT)
		{
			lpLayout->sizeTotal.cx += size.cx;
			lpLayout->sizeTotal.cy = max(lpLayout->sizeTotal.cy, size.cy);
			if (dwStyle & CBRS_ALIGN_LEFT)
				lpLayout->rect.left += size.cx;
			else if (dwStyle & CBRS_ALIGN_RIGHT)
			{
				rect.left = rect.right - size.cx;
				lpLayout->rect.right -= size.cx;
			}
		}
		else
		{
			ASSERT(FALSE);      // can never happen
		}

		rect.right = rect.left + size.cx;
		rect.bottom = rect.top + size.cy;

		// only resize the window if doing layout and not just rect query
		if (lpLayout->hDWP != NULL)
			AfxRepositionWindow(lpLayout, m_hWnd, &rect);
	}
	return 0;
}

可以看到,这个函数主要就是根据 dwStyle 来计算窗口位置和大小,而dwStyle就是状态栏、菜单栏这些控件的样式,在Create时就已经默认定好了。

下面是状态栏的Create函数,第2个参数就是dwStyle:

//CStatusBar::Create
virtual BOOL Create(
    CWnd* pParentWnd,
    DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM,
    UINT nID = AFX_IDW_STATUS_BAR);

这个CBRS_BOTTOM样式就是调用RepositionBars计算状态栏位置的关键,这个样式表示控件位于窗口的底部,所以计算状态栏位置的时候:

else if (dwStyle & CBRS_ALIGN_BOTTOM)
{
	rect.top = rect.bottom - size.cy;
	lpLayout->rect.bottom -= size.cy;
}

即:状态栏顶部y坐标 = 窗口底部y坐标 - 状态栏的高度

同理,调用RepositionBars工具栏会移动到窗口客户区最上面,也是因为样式CBRS_TOP:

//CToolBar::Create
virtual BOOL Create(
    CWnd* pParentWnd,
    DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP,
    UINT nID = AFX_IDW_TOOLBAR);

因此,调用一次RepositionBars,会导致状态栏移动到窗口底部,工具栏移动到窗口顶部。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值