IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> MFC对话框分割条拖动控件的实现 -> 正文阅读

[C++知识库]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);
	
	//省略... ....
}

即可实现文初效果。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 22:13:21  更:2022-03-15 22:15:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:29:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码