赞
踩
MFC中,常需要在对话框中添加一个分割条,拖动分割条时动态改变各个控件的大小和位置,本文通过从CStatic控件派生一个新类的方式实现这一功能,效果如下:
从CWnd派生一个类,作为分割条的实现类。其头文件为:
#pragma once class CVertSplitter :public CStatic { DECLARE_DYNAMIC(CVertSplitter) public: CVertSplitter(); ~CVertSplitter(); //设置移动分割条过程中对话框两侧的最小距离 void SetMinWidth(int left, int right); //分割条左侧的控件ID BOOL AttachCtrlAsLeftPane(DWORD idCtrl); //分割条右侧的控件ID BOOL AttachCtrlAsRightPane(DWORD idCtrl); //拆离分割条左右两侧的控件 BOOL DetachAllPanes(); //根据分割条位置,调整对话框上所有控件位置 void AdjustLayout(); protected: //分割条可以移动的范围 BOOL GetMouseClipRect(LPRECT rcClip, CPoint point); afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); DECLARE_MESSAGE_MAP() private: CRect m_rcOrgRect; CRect m_rcOldRect; CWnd* m_pParent; CPoint m_pPointStart; int m_iLeftMin, m_iRightMin; CDWordArray m_idLeft, m_idRight; };
其具体的实现文件为如下。这里特别注意OnLButtonDown中捕获鼠标输入的函数,以及OnLButtonUp中解除鼠标捕获的函数。上述操作的作用:当用户在分割条按下鼠标左键后,分割条开始捕获光标的移动操作,直到鼠标左键弹起。
#include "pch.h" #include "CVertSplitter.h" IMPLEMENT_DYNAMIC(CVertSplitter, CStatic) CVertSplitter::CVertSplitter() :CStatic() , m_pParent(NULL) , m_iLeftMin(10) , m_iRightMin(10) {} CVertSplitter::~CVertSplitter() {} void CVertSplitter::SetMinWidth(int left, int right) { m_iLeftMin = left; m_iRightMin = right; } BOOL CVertSplitter::AttachCtrlAsLeftPane(DWORD idCtrl) { m_idLeft.Add(idCtrl); return TRUE; } BOOL CVertSplitter::AttachCtrlAsRightPane(DWORD idCtrl) { m_idRight.Add(idCtrl); return TRUE; } BOOL CVertSplitter::DetachAllPanes() { m_idLeft.RemoveAll(); m_idRight.RemoveAll(); return TRUE; } void CVertSplitter::AdjustLayout() { CWnd* pane; RECT rcBar, rcPane; GetWindowRect(&rcBar); m_pParent->ScreenToClient(&rcBar); int i; DWORD id; for (i = 0; i < m_idLeft.GetSize(); i++) { id = m_idLeft.GetAt(i); pane = m_pParent->GetDlgItem(id); pane->GetWindowRect(&rcPane); m_pParent->ScreenToClient(&rcPane); rcPane.right = rcBar.left - 1; pane->MoveWindow(&rcPane, FALSE); } for (i = 0; i < m_idRight.GetSize(); i++) { id = m_idRight.GetAt(i); pane = m_pParent->GetDlgItem(id); pane->GetWindowRect(&rcPane); m_pParent->ScreenToClient(&rcPane); rcPane.left = rcBar.right + 1; pane->MoveWindow(&rcPane, FALSE); } m_pParent->Invalidate(); } BEGIN_MESSAGE_MAP(CVertSplitter, CStatic) ON_WM_SETCURSOR() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() END_MESSAGE_MAP() BOOL CVertSplitter::GetMouseClipRect(LPRECT rcClip, CPoint point) { RECT rcOrg, rcTarget, rcParent, rcPane; DWORD id; GetWindowRect(&rcOrg); m_pParent->GetClientRect(&rcParent); m_pParent->ClientToScreen(&rcParent); rcTarget = rcOrg; rcTarget.left = rcParent.left + m_iLeftMin; for (int i = 0; i < m_idLeft.GetSize(); i++) { id = m_idLeft.GetAt(i); m_pParent->GetDlgItem(id)->GetWindowRect(&rcPane); if (rcTarget.left < rcPane.left + m_iLeftMin) { rcTarget.left = rcPane.left + m_iLeftMin; } } rcTarget.right = rcParent.right - m_iRightMin; for (int i = 0; i < m_idRight.GetSize(); i++) { id = m_idRight.GetAt(i); m_pParent->GetDlgItem(id)->GetWindowRect(&rcPane); if (rcTarget.right > rcPane.right - m_iRightMin) { rcTarget.right = rcPane.right - m_iRightMin; } } if (rcTarget.left >= rcTarget.right) { TRACE(_T("No room to drag the x-splitter bar")); return FALSE; } //point指的是窗口的客户坐标,而不是屏幕坐标 rcClip->left = rcTarget.left + point.x; rcClip->right = rcTarget.right - (rcOrg.right - rcOrg.left - point.x) + 1; rcClip->top = rcOrg.top; rcClip->bottom = rcOrg.bottom; return TRUE; } BOOL CVertSplitter::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { //以下非常奇怪,用全局函数可以,但是用CStatic方法却不行 //if (GetCursor() == NULL) // SetCursor(::LoadCursor(NULL, IDC_HELP)); ::SetCursor(::LoadCursor(NULL, IDC_SIZEWE)); return TRUE; } void CVertSplitter::OnLButtonDown(UINT nFlags, CPoint point) { //如果鼠标被别的程序捕获,不再处理此消息 if (::GetCapture() != NULL) return; m_pParent = GetParent(); if (!m_pParent) return; //ClipCursor限定鼠标移动范围 CRect rcMouseClip; if (!GetMouseClipRect(rcMouseClip, point)) return; ::ClipCursor(&rcMouseClip); m_pPointStart = point; SetCapture(); //捕获鼠标输入 GetWindowRect(m_rcOrgRect); m_pParent->ScreenToClient(m_rcOrgRect); CDC* pDrawDC = NULL; pDrawDC = m_pParent->GetDC(); pDrawDC->DrawDragRect(m_rcOrgRect, CSize(1, 1), NULL, CSize(1, 1)); m_rcOldRect = m_rcOrgRect; m_pParent->ReleaseDC(pDrawDC); } void CVertSplitter::OnMouseMove(UINT nFlags, CPoint point) { if (GetCapture() == this) { CDC* pDrawDC = NULL; pDrawDC = m_pParent->GetDC(); CRect rcCur = m_rcOrgRect; long xDiff = 0, yDiff = 0; xDiff = point.x - m_pPointStart.x; yDiff = point.y - m_pPointStart.y; rcCur.OffsetRect(xDiff, 0); pDrawDC->DrawDragRect(rcCur, CSize(1, 1), &m_rcOldRect, CSize(1, 1)); m_rcOldRect = rcCur; m_pParent->ReleaseDC(pDrawDC); } } void CVertSplitter::OnLButtonUp(UINT nFlags, CPoint point) { if (GetCapture() == this) { CDC* pDrawDC = NULL; pDrawDC = m_pParent->GetDC(); //获取DC //可以采用下列两种方式之一 //pDrawDC->DrawDragRect(m_rcOldRect, CSize(1, 1), NULL, CSize(1, 1)); pDrawDC->DrawDragRect(CRect(0, 0, 0, 0), CSize(1, 1), m_rcOldRect, CSize(1, 1)); m_pParent->ReleaseDC(pDrawDC); ::ReleaseCapture(); MoveWindow(m_rcOldRect); AdjustLayout(); } ::ClipCursor(NULL); }
在对话框资源编辑器中,拖入一个picture控件,ID设为IDC_PIC_SPLITTER,type属性可选为Etched Vert,Notify属性必须为True.
在对话框类的头文件中,加入
private:
CVertSplitter m_wndXSplitter;
在对话框类的DoDataExchange函数中,加入
void CSplitterWndDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_PIC_SPLITTER, m_wndXSplitter);
}
在对话框类的OnInitDialog函数中,加入
BOOL CSplitterWndDlg::OnInitDialog()
{
//省略... ....
// TODO: 在此添加额外的初始化代码
m_wndXSplitter.AttachCtrlAsLeftPane(IDC_BTN);
m_wndXSplitter.AttachCtrlAsLeftPane(IDC_EDT);
m_wndXSplitter.AttachCtrlAsLeftPane(IDC_TRE);
m_wndXSplitter.AttachCtrlAsRightPane(IDC_PIC_PREVIEW);
//省略... ....
}
即可实现文初效果。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。