00. 目录

 

 

01. 案例概述

由MFC向导创建的文档视图结构应用程序,其创建的工具栏左侧只有一条竖线,实例实现将这一条竖线,改为两条竖线。效果如下图所示。
【MFC】工具栏左侧双线效果_vs2019

02. 开发环境

系统环境:Windows 10

开发环境:Visual Studio 2019

03. 关键技术

绘制普通线条使用CDC类的LineTo方法即可,如果是立体效果的线条就需要绘制多条不同颜色的线条,把一个线条看做是一个矩形区域,它的左边缘和下边缘使用COLOR_BTNSHADOW颜色,右边缘和上边缘使用COLOR_BTNHILIGHT颜色,这样就可以实现线条突出的效果。

04. 程序设计

(1)新建一个基于单文档视图结构的应用程序。

(2)由CToolBar派生一个新类CMyToolBar。

(3)在新类CMyToolBar的DrawGripper函数中,实现双线的绘制,实现代码如下。

CMyToolBar.h

// CMyToolBar

class CMyToolBar : public CToolBar
{
	DECLARE_DYNAMIC(CMyToolBar)

public:
	CMyToolBar();
	virtual ~CMyToolBar();

	void Draw3dRect(CDC* pDC, CRect rc) const;



protected:
	void DrawBorders(CDC* pDC, CRect& rect);
	void EraseNonClient(BOOL);
	void DrawGripper(CDC &dc) const;

	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnNcPaint();

	DECLARE_MESSAGE_MAP()
};

CMyToolBar.cpp

// CMyToolBar.cpp: 实现文件
//
#include "pch.h"
#include "5Edit.h"
#include "CMyToolBar.h"
// CMyToolBar
IMPLEMENT_DYNAMIC(CMyToolBar, CToolBar)

CMyToolBar::CMyToolBar()
{

}

CMyToolBar::~CMyToolBar()
{
}

void CMyToolBar::DrawGripper(CDC& dc) const
{
	// no gripper if floating
	if (IsFloating()) {
		return;
	}

	if (m_dwStyle & CBRS_GRIPPER)
	{
		CRect gripper;
		GetWindowRect(gripper);
		ScreenToClient(gripper);
		gripper.OffsetRect(-gripper.left, -gripper.top);

		if (m_dwStyle & CBRS_ORIENT_HORZ) {

			// gripper at left
			gripper.DeflateRect(4, 3);
			gripper.right = gripper.left + 3;
			gripper.bottom += 1;
			Draw3dRect(&dc, gripper);
		}
		else {
			// gripper at top
			gripper.DeflateRect(3, 4);
			gripper.top -= 1;
			gripper.bottom = gripper.top + 3;
			Draw3dRect(&dc, gripper);
		}
	}
}
void CMyToolBar::DrawBorders(CDC* pDC, CRect& rect)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);

	DWORD dwStyle = m_dwStyle;
	if (!(dwStyle & CBRS_BORDER_ANY))
		return;

	// prepare for dark lines
	ASSERT(rect.top == 0 && rect.left == 0);

	COLORREF clr = GetSysColor(COLOR_3DSHADOW);
	if (dwStyle & CBRS_BORDER_RIGHT)
		pDC->FillSolidRect(rect.right - 1, 0, rect.right, rect.bottom, clr); //right
	if (dwStyle & CBRS_BORDER_BOTTOM)
		pDC->FillSolidRect(0, rect.bottom - 1, rect.right, rect.bottom, clr); //bottom

	clr = GetSysColor(COLOR_3DHIGHLIGHT);
	if (dwStyle & CBRS_BORDER_TOP)
		pDC->FillSolidRect(0, 0, rect.right, 1, clr); //top
	if (dwStyle & CBRS_BORDER_LEFT)
		pDC->FillSolidRect(0, 0, 1, rect.bottom, clr); //left

	if (dwStyle & CBRS_BORDER_TOP)
		rect.top++;
	if (dwStyle & CBRS_BORDER_RIGHT)
		rect.right--;
	if (dwStyle & CBRS_BORDER_BOTTOM)
		rect.bottom--;
	if (dwStyle & CBRS_BORDER_LEFT)
		rect.left++;
}
void CMyToolBar::Draw3dRect(CDC* pDC, CRect rc) const
{
	static bool bDraw2nd = true;

	if (bDraw2nd == true) {
		rc.right -= 1;
		rc.bottom -= 1;
	}

	// Get a pen for hilite and shadow.
	CPen penHilite(PS_SOLID, 1, ::GetSysColor(COLOR_BTNHILIGHT));
	CPen penShadow(PS_SOLID, 1, ::GetSysColor(COLOR_BTNSHADOW));

	// Set up points for line draw.
	CPoint ptTopLeft(rc.left, rc.top);
	CPoint ptTopRight(rc.right, rc.top);
	CPoint ptBottomLeft(rc.left, rc.bottom);
	CPoint ptBottomRight(rc.right, rc.bottom);

	// Select the shadow pen, and draw the bottom right.
	pDC->SelectObject(&penShadow);

	pDC->MoveTo(ptTopRight);
	pDC->LineTo(ptBottomRight);
	pDC->LineTo(ptBottomLeft);

	// Select the hilite pen, and draw the top left.
	pDC->SelectObject(&penHilite);

	pDC->MoveTo(ptBottomLeft);
	pDC->LineTo(ptTopLeft);
	pDC->LineTo(ptTopRight);

	// Draw the second gripper line.
	if (bDraw2nd == true)
	{
		bDraw2nd = false;
		if (m_dwStyle & CBRS_ORIENT_HORZ)
			rc.OffsetRect(3, 0);
		else
			rc.OffsetRect(0, 3);
		Draw3dRect(pDC, rc);
		bDraw2nd = true;
	}
}
void CMyToolBar::EraseNonClient(BOOL)
{
	// get window DC that is clipped to the non-client area
	CWindowDC dc(this);
	CRect rectClient;
	GetClientRect(rectClient);
	CRect rectWindow;
	GetWindowRect(rectWindow);
	ScreenToClient(rectWindow);
	rectClient.OffsetRect(-rectWindow.left, -rectWindow.top);
	dc.ExcludeClipRect(rectClient);     // draw borders in non-client area

	// draw borders in non-client area
	rectWindow.OffsetRect(-rectWindow.left, -rectWindow.top);
	DrawBorders(&dc, rectWindow);     // erase parts not drawn
	dc.IntersectClipRect(rectWindow);
	SendMessage(WM_ERASEBKGND, (WPARAM)dc.m_hDC);
	DrawGripper(dc);
}

int CMyToolBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CToolBar::OnCreate(lpCreateStruct) == -1)
		return -1;
	SendMessage(TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS);

	ModifyStyle(0, TBSTYLE_FLAT); // flat with gripper.
	m_dwStyle |= CBRS_GRIPPER;//m_dwStyle CControlBar的成员变量

	return 0;
}

void CMyToolBar::OnNcPaint()
{
	CControlBar::EraseNonClient();
	CWindowDC dc(this);

	CRect pRect;
	GetClientRect(&pRect);
	InvalidateRect(&pRect, TRUE);
	EraseNonClient(FALSE);
}

BEGIN_MESSAGE_MAP(CMyToolBar, CToolBar)
	ON_WM_CREATE()
	ON_WM_NCPAINT()
END_MESSAGE_MAP()

// CMyToolBar 消息处理程序

MainFrm.cpp文件

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | 
		CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
		!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
	{
		TRACE0("未能创建工具栏\n");
		return -1;      // 未能创建
	}

	if (!m_wndStatusBar.Create(this))
	{
		TRACE0("未能创建状态栏\n");
		return -1;      // 未能创建
	}
	m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));

	// TODO: 如果不需要可停靠工具栏,则删除这三行
	m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	EnableDocking(CBRS_ALIGN_ANY);
	DockControlBar(&m_wndToolBar);

	return 0;
}

05. 秘笈心法

Draw3dRect方法的使用

CDC类Draw3dRect方法可以用来绘制区域,使用Draw3dRect方法可以绘制按钮控件的效果,但该方法绘制不了线条,只能用在控件自绘的过程中,掌握Draw3dRect方法可以快速绘制突出效果的矩形区域。