MFC对话框分割条拖动控件的实现

MFC中,常需要在对话框中添加一个分割条,拖动分割条时动态改变各个控件的大小和位置,本文通过从CStatic控件派生一个新类的方式实现这一功能,效果如下:
在这里插入图片描述

一 实现对话框分割条所需的几个函数

1 获取与设定窗口样式

  1. DWORD CWnd::GetStyle ( ) const
    该函数可以获取对话框当前的窗口样式,注意这个窗口样式包含窗口通用的样式以及控件独有的样式。
  2. LONG GetWindowLong( HWND hWnd, int nIndex)
    这个函数可以获取指定窗口的属性,当然也包括窗口的样式
  3. LONG SetWindowLong( HWND hWnd, int nIndex, LONG dwNewLong);
    这个函数可以获取指定窗口的属性,当然包括窗口的样式。

2 捕获鼠标消息

  1. HWND GetCapture(VOID);
    该函数用于查看当前捕获鼠标的窗口句柄。每次只能有一个窗口能够捕获鼠标;当窗口捕获鼠标输入时,无论鼠标是否在窗口边界以内,窗口均可以接收到鼠标输入。如果没有窗口捕获鼠标,这个函数返回空。(The GetCapture function retrieves a handle to the window (if any) that has captured the mouse. Only one window at a time can capture the mouse; this window receives mouse input whether or not the cursor is within its borders. )
  2. CWnd* CWnd::SetCapture ( );
    该函数使后续的所有鼠标输入都发送到当前的窗口,无论鼠标位置在哪。当该窗口不需要捕获所有的鼠标输入时,程序应当调用ReleaseCapture ,这样其他窗口就能够接受到鼠标输入了。(Causes all subsequent mouse input to be sent to the current CWnd object regardless of the position of the cursor. When CWnd no longer requires all mouse input, the application should call the ReleaseCapture function so that other windows can receive mouse input. )

3 限定光标移动范围

  1. BOOL ClipCursor( const RECT *lpRect);
    该函数限定光标在屏幕上的一个矩形区域内活动。如果光标落在这个矩形区域以外,系统自动调整光标位置,使它重新落在矩形区域以内。参数指示矩形区域的范围,如果为NULL,取消范围限制,光标可以自由活动。
    另外,光标是共享资源。对一个限定光标运动范围的程序,如果需要当将光标控制权移交给其他程序,那么必须调用ClipCursor。
    (The ClipCursor function confines the cursor to a rectangular area on the screen. If a subsequent cursor position (set by the SetCursorPos function or the mouse) lies outside the rectangle, the system automatically adjusts the position to keep the cursor inside the rectangular area.
    The cursor is a shared resource. If an application confines the cursor, it must release the cursor by using ClipCursor before relinquishing control to another application. )

二 对话框分割条的实现类

从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);
	
	//省略... ....
}

即可实现文初效果。

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
您好!对于C++ MFC对话框中的鼠标拖动和缩放控件中的图片,可以使用以下步骤实现: 1. 在对话框中添加一个图片控件,可以使用MFC中提供的CStatic类,并设置图片的初始位置和大小。 2. 通过WM_LBUTTONDOWN和WM_LBUTTONUP消息,实现鼠标拖动图片的功能。在WM_LBUTTONDOWN消息中,记录下鼠标点击时的位置,并设置一个标志位表示当前正在拖动图片。在WM_LBUTTONUP消息中,清除标志位。 3. 通过WM_MOUSEWHEEL消息,实现鼠标滚轮缩放图片的功能。在WM_MOUSEWHEEL消息中,获取鼠标滚轮滚动的距离,根据距离缩放图片的大小,并重新设置图片的位置。 4. 在对话框的OnPaint()函数中,绘制图片。 以上是实现鼠标拖动和缩放控件中的图片的基本步骤,您可以参考以下代码示例: ``` // 在对话框头文件中添加以下变量 private: CStatic m_picCtrl; // 图片控件 CPoint m_ptOrigin; // 鼠标点击时的位置 BOOL m_bDragging; // 是否正在拖动图片 CSize m_szPic; // 图片大小 // 在OnInitDialog()函数中初始化图片控件,并设置图片的初始位置和大小 m_picCtrl.Create(NULL, WS_VISIBLE | WS_CHILD | SS_BITMAP, CRect(0, 0, 0, 0), this); m_picCtrl.SetBitmap(/* 设置图片 */); m_szPic = /* 设置图片大小 */; m_picCtrl.MoveWindow(/* 设置图片初始位置和大小 */); // 处理鼠标消息 void CMyDialog::OnLButtonDown(UINT nFlags, CPoint point) { m_ptOrigin = point; m_bDragging = TRUE; } void CMyDialog::OnLButtonUp(UINT nFlags, CPoint point) { m_bDragging = FALSE; } void CMyDialog::OnMouseMove(UINT nFlags, CPoint point) { if (m_bDragging) { CPoint ptOffset = point - m_ptOrigin; m_picCtrl.MoveWindow(m_picCtrl.GetRect() + ptOffset); } } void CMyDialog::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { double fScale = zDelta > 0 ? 1.1 : 0.9; m_szPic.cx = (int)(m_szPic.cx * fScale); m_szPic.cy = (int)(m_szPic.cy * fScale); m_picCtrl.MoveWindow(/* 计算并设置图片的位置和大小 */); } // 绘制图片 void CMyDialog::OnPaint() { CPaintDC dc(this); CDC dcMem; dcMem.CreateCompatibleDC(&dc); CBitmap* pOldBitmap = dcMem.SelectObject(/* 获取图片的位图句柄 */); dc.BitBlt(/* 绘制图片 */); dcMem.SelectObject(pOldBitmap); } ``` 希望对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Santiago

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值