当前位置:   article > 正文

MFC对话框分割条拖动控件的实现_mfc基于对话框分割窗口

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;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

其具体的实现文件为如下。这里特别注意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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198

三 分割条类的使用

在对话框资源编辑器中,拖入一个picture控件,ID设为IDC_PIC_SPLITTER,type属性可选为Etched Vert,Notify属性必须为True.

在这里插入图片描述

在对话框类的头文件中,加入

private:
	CVertSplitter m_wndXSplitter;
  • 1
  • 2

在对话框类的DoDataExchange函数中,加入

void CSplitterWndDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_PIC_SPLITTER, m_wndXSplitter);
}
  • 1
  • 2
  • 3
  • 4
  • 5

在对话框类的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);
	
	//省略... ....
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

即可实现文初效果。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/144180
推荐阅读
相关标签
  

闽ICP备14008679号