目录
继承CMFCTabCtrl实现了一些常用的效果,包括官方未实现每个Tab自己的关闭按钮功能
-
一、实现效果
这种Tab有自己独立关闭按钮的响应事件官方并没有实现,需要自己去OnLButtonUp里实现
-
二、风格
共有8种风格:
enum Style
{
STYLE_3D = 0,
STYLE_FLAT = 1,
STYLE_FLAT_SHARED_HORZ_SCROLL = 2,
STYLE_3D_SCROLLED = 3,
STYLE_3D_ONENOTE = 4,
STYLE_3D_VS2005 = 5,
STYLE_3D_ROUNDED = 6,
STYLE_3D_ROUNDED_SCROLL = 7,
};
其中,STYLE_3D和STYLE_3D_ROUNDED是没有独立的关闭按钮的
STYLE_3D
STYLE_FLAT
STYLE_FLAT_SHARED_HORZ_SCROLL
STYLE_3D_SCROLLED
STYLE_3D_ONENOTE
STYLE_3D_VS2005
STYLE_3D_ROUNDED
STYLE_3D_ROUNDED_SCROLL
-
三、代码目录
-
四、源码
MFCTabCtrlEx.h
#pragma once
#define WN_MSG_DELETE_TAB WM_USER + 654
// CMFCTabCtrlEx
class CMFCTabCtrlEx : public CMFCTabCtrl
{
DECLARE_DYNAMIC(CMFCTabCtrlEx)
public:
CMFCTabCtrlEx();
virtual ~CMFCTabCtrlEx();
void SetTabsHeight(int nHeight);
protected:
DECLARE_MESSAGE_MAP()
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
afx_msg void OnDeleteTab();
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
};
MFCTabCtrlEx.cpp
// MFCTabCtrlEx.cpp : 实现文件
//
#include "stdafx.h"
#include "MFCTabCtrlEx.h"
#define MENU_ID_DELETE_TAB WM_USER + 524
// CMFCTabCtrlEx
IMPLEMENT_DYNAMIC(CMFCTabCtrlEx, CMFCTabCtrl)
CMFCTabCtrlEx::CMFCTabCtrlEx()
{
}
CMFCTabCtrlEx::~CMFCTabCtrlEx()
{
}
void CMFCTabCtrlEx::SetTabsHeight(int nHeight)
{
m_nTabsHeight = nHeight;
}
BEGIN_MESSAGE_MAP(CMFCTabCtrlEx, CMFCTabCtrl)
ON_WM_CREATE()
ON_WM_CONTEXTMENU()
ON_COMMAND(MENU_ID_DELETE_TAB, &CMFCTabCtrlEx::OnDeleteTab)
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
// CMFCTabCtrlEx 消息处理程序
int CMFCTabCtrlEx::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMFCTabCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
CArray<COLORREF, COLORREF> arColors;
arColors.Add(RGB(121, 210, 231));
arColors.Add(RGB(135, 206, 250));
arColors.Add(RGB(176, 196, 222));
EnableAutoColor(TRUE);
SetAutoColors(arColors);
EnableTabSwap(TRUE);
SetActiveTabBoldFont(TRUE);
SetTabBorderSize(0);
SetTabsHeight(30);
HideNoTabs(TRUE);
EnableActiveTabCloseButton(TRUE); // 右上角的统一关闭按钮
return 0;
}
BOOL CMFCTabCtrlEx::OnCommand(WPARAM wParam, LPARAM lParam)
{
// TODO: 在此添加专用代码和/或调用基类
if ((HWND)lParam == m_btnClose.GetSafeHwnd())
{
if (GetParent() != nullptr)
{
GetParent()->PostMessage(WN_MSG_DELETE_TAB);
}
return TRUE;
}
return CMFCTabCtrl::OnCommand(wParam, lParam);
}
void CMFCTabCtrlEx::OnContextMenu(CWnd* pWnd, CPoint point)
{
// TODO: 在此处添加消息处理程序代码
CPoint pt(point);
ScreenToClient(&pt);
int iTab = GetTabFromPoint(pt);
if (iTab < 0)
{
return;
}
SetActiveTab(iTab);
CMenu menu;
menu.CreatePopupMenu(); // 创建弹出菜单
menu.AppendMenu(MF_STRING, MENU_ID_DELETE_TAB, "关闭");
ClientToScreen(&pt);
menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
menu.DestroyMenu();
CMFCTabCtrl::OnContextMenu(pWnd, point);
}
void CMFCTabCtrlEx::OnDeleteTab()
{
int nIndex = GetActiveTab();
if (nIndex < 0)
{
return ;
}
RemoveTab(nIndex);
}
void CMFCTabCtrlEx::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CMFCTabCtrl::OnLButtonUp(nFlags, point);
// 获取Tab的数量
if (GetTabsNum() == 0)
return;
// 获取关闭按钮的区域
CRect rcCloseBtn = GetTabCloseButton();
if (rcCloseBtn.PtInRect(point))
{
if (MessageBox(_T("确认关闭该标签页?"), _T("关闭提示"), MB_YESNO) == IDYES)
{
// 关闭当前Tab页关联窗口
// 获取当前选择的Tab
int nSel = GetActiveTab();
GetActiveWnd()->SendMessage(WM_CLOSE);
// 删除当前Tab
RemoveTab(nSel);
}
}
}
CMFCTabCtrlDemoDlg.h
// CMFCTabCtrlDemoDlg.h : 头文件
//
#pragma once
#include "MFCTabCtrlEx.h"
class CFirstDlg;
class CSecondDlg;
// CCMFCTabCtrlDemoDlg 对话框
class CCMFCTabCtrlDemoDlg : public CDialogEx
{
// 构造
public:
CCMFCTabCtrlDemoDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_CMFCTABCTRLDEMO_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
afx_msg LRESULT OnDeleteTab(WPARAM wParam, LPARAM lParam);
CMFCTabCtrlEx m_wndTab;
CFirstDlg* m_pFirstDlg;
CSecondDlg* m_pSecondDlg;
};
CMFCTabCtrlDemoDlg.cpp
// CMFCTabCtrlDemoDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "CMFCTabCtrlDemo.h"
#include "CMFCTabCtrlDemoDlg.h"
#include "afxdialogex.h"
#include "FirstDlg.h"
#include "SecondDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CCMFCTabCtrlDemoDlg 对话框
CCMFCTabCtrlDemoDlg::CCMFCTabCtrlDemoDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_CMFCTABCTRLDEMO_DIALOG, pParent)
, m_pFirstDlg(nullptr)
, m_pSecondDlg(nullptr)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CCMFCTabCtrlDemoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CCMFCTabCtrlDemoDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WN_MSG_DELETE_TAB, &CCMFCTabCtrlDemoDlg::OnDeleteTab)
END_MESSAGE_MAP()
// CCMFCTabCtrlDemoDlg 消息处理程序
BOOL CCMFCTabCtrlDemoDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
CRect rectTab;
GetClientRect(&rectTab);
// STYLE_3D、STYLE_3D_ROUNDED 不带关闭按钮
m_wndTab.Create(CMFCTabCtrl::STYLE_3D_ONENOTE, rectTab, this, WM_USER + 521, CMFCTabCtrl::LOCATION_TOP, TRUE);
m_pFirstDlg = new CFirstDlg();
m_pSecondDlg = new CSecondDlg();
m_pFirstDlg->Create(IDD_DIALOG1, &m_wndTab);
m_pSecondDlg->Create(IDD_DIALOG2, &m_wndTab);
m_wndTab.AddTab(m_pFirstDlg, "First Dialog");
m_wndTab.AddTab(m_pSecondDlg, "Second Dialog");
m_wndTab.SetActiveTab(m_wndTab.GetTabsNum() - 1);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CCMFCTabCtrlDemoDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CCMFCTabCtrlDemoDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CCMFCTabCtrlDemoDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
LRESULT CCMFCTabCtrlDemoDlg::OnDeleteTab(WPARAM wParam, LPARAM lParam)
{
int nIndex = m_wndTab.GetActiveTab();
if (nIndex < 0)
{
return 0;
}
m_wndTab.RemoveTab(nIndex);
return 0;
}
参考:
https://blog.csdn.net/ilson_/article/details/80655010#comments