00. 目录
01. 案例概述
由MFC向导创建的文档视图结构应用程序,其创建的工具栏左侧只有一条竖线,实例实现将这一条竖线,改为两条竖线。效果如下图所示。
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方法可以快速绘制突出效果的矩形区域。