[MFC] 툴바 붙이기 – CToolBarCtrl

회사에서 뷰 화면에 메뉴를 ‘이쁘게’ 만들어 달라는 요구사항이 들어왔습니다. MFC 화면에 이쁘게라니.. 
요구 사항이니 CToolBarCtrl 사용하여 아래와 같은 화면을 만들어 보겠습니다.

메시지 정의, 멤버 변수&함수 선언

먼저 뷰나 대화상자를 생성한 뒤 헤더파일에 아래와 같이 메시지들을 정의합니다.

// 클릭 메시지
#define WM_COMMAND1		(WM_USER + 1)
#define WM_COMMAND2		(WM_USER + 2)
#define WM_COMMAND3		(WM_USER + 3)
#define WM_COMMAND4		(WM_USER + 4)
#define WM_COMMAND5		(WM_USER + 5)
#define WM_COMMAND6		(WM_USER + 6)
#define WM_COMMAND7		(WM_USER + 7)

// 메뉴
#define WM_MENU1_1		(WM_USER + 13)
#define WM_MENU1_2		(WM_USER + 14)
#define WM_MENU1_3		(WM_USER + 15)
#define WM_MENU2_1		(WM_USER + 16)
#define WM_MENU2_2		(WM_USER + 17)
#define WM_MENU3		(WM_USER + 18)

멤버 변수도 선언해줍니다.

CToolBarCtrl m_toolbar; // 툴바 컨트롤
CImageList *m_pIconList; // 이미지 리스트
CArray<int, int> m_arrIcons; // 툴바 이미지
CArray<CString, CString> m_arrString; // 툴바 버튼 문자열
CArray<int, int> m_arrCommand; // 툴바 클릭 명령 처리

이 변수들은 생성자와 소멸자에 아래와 같이 코딩해주세요

// 생성자
m_pIconList = new CImageList;

m_arrIcons.RemoveAll();
m_arrIcons.Add(IDI_ICON1);
m_arrIcons.Add(IDI_ICON2);
m_arrIcons.Add(IDI_ICON3);
m_arrIcons.Add(IDI_ICON4);
m_arrIcons.Add(IDI_ICON5);
m_arrIcons.Add(IDI_ICON6);
m_arrIcons.Add(IDI_ICON7);

m_arrString.RemoveAll();
m_arrString.Add(_T("버튼1"));
m_arrString.Add(_T("버튼2"));
m_arrString.Add(_T("버튼3"));
m_arrString.Add(_T("버튼4"));
m_arrString.Add(_T("버튼5"));
m_arrString.Add(_T("버튼6"));
m_arrString.Add(_T("버튼7"));

m_arrCommand.RemoveAll();
m_arrCommand.Add(WM_COMMAND1);
m_arrCommand.Add(WM_COMMAND2);
m_arrCommand.Add(WM_COMMAND3);
m_arrCommand.Add(WM_COMMAND4);
m_arrCommand.Add(WM_COMMAND5);
m_arrCommand.Add(WM_COMMAND6);
m_arrCommand.Add(WM_COMMAND7);
// 소멸자
if (m_pIconList != NULL)
{
	m_pIconList->DeleteImageList();
	delete m_pIconList;
}
m_pIconList = NULL;

m_arrIcons.RemoveAll();
m_arrString.RemoveAll();
m_arrCommand.RemoveAll();

각 동작에 대한 멤버 함수들도 선언해줍니다.

void CreateToolBarByIcon(void); // 툴바 생성
afx_msg void OnBnClickedByIcon(UINT uiRes); // 버튼 클릭
afx_msg BOOL OnNotifyToolBar(UINT uiID, NMHDR * pNMHDR, LRESULT * pResult); // 마우스 오버시 툴팁
afx_msg BOOL OnToolbarDropDown(UINT uiID, NMHDR * pNMHDR, LRESULT * pResult); // 드롭다운 버튼클릭 메뉴 표시
툴바 생성

우선 툴바를 생성해보겠습니다. CreateToolBar를 아래와 같이 정의해주고, 뷰는 OnInitialUpdate에서 , 대화상자에서는 OnInitDialog에서 호출해주세요.

void CMFCApplication1Dlg::CreateToolBarByIcon(void)
{
	// 툴바 생성
	CRect rectGw(0, 0, 0, 0);
	BOOL bCreate = m_toolbar.Create(TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_TRANSPARENT | WS_CHILD | WS_VISIBLE | WS_BORDER | CCS_TOP,
									rectGw, this, IDR_TOOLBAR1);
	if (!bCreate)
		return;

	// 드롭다운 화살표 표시를 위한 스타일 변경
	DWORD dwStyle = m_toolbar.GetExtendedStyle();
	m_toolbar.SetExtendedStyle(dwStyle | TBSTYLE_EX_DRAWDDARROWS);

	// 툴바에 사용되는 이미지 설정
	int iIconCount = m_arrIcons.GetCount();
	m_pIconList->Create(16, 16, ILC_COLOR32 | ILC_MASK, iIconCount, 0); // 이미지리스트 생성
	for (int i = 0 ; i < iIconCount ; i++)
		m_pIconList->Add(AfxGetApp()->LoadIcon(m_arrIcons.GetAt(i))); // icon 로드
	m_toolbar.SetImageList(m_pIconList); // 이미지리스트 툴바에 설정
	
	// 버튼 설정
	for (int iIndex = 0; iIndex < iIconCount ; iIndex++)
	{
		if (iIndex <= 4)
		{
			// 버튼 추가
			TBBUTTON TBButton;
			memset(&TBButton, 0, sizeof(TBButton));
			TBButton.iString = m_toolbar.AddStrings(m_arrString.GetAt(iIndex));
			TBButton.fsState = TBSTATE_ENABLED;
			TBButton.fsStyle = TBSTYLE_BUTTON;
			TBButton.iBitmap = iIndex;
			TBButton.idCommand = m_arrCommand.GetAt(iIndex);
			m_toolbar.AddButtons(1, &TBButton);
		}
		else
		{
			// 버튼과 드롭다운 분리
			if (iIndex == 5)
			{
				TBBUTTON sepButton;
				memset(&sepButton, 0, sizeof(sepButton));
				sepButton.fsStyle = TBSTYLE_SEP;
				m_toolbar.AddButtons(1, &sepButton);
			}

			// 드롭다운 버튼 추가
			TBBUTTON TBButton;
			memset(&TBButton, 0, sizeof(TBButton));
			TBButton.iString = m_toolbar.AddStrings(m_arrString.GetAt(iIndex));
			TBButton.fsState = TBSTATE_ENABLED;
			TBButton.fsStyle = TBSTYLE_BUTTON | TBSTYLE_DROPDOWN; 
			TBButton.iBitmap = iIndex;
			TBButton.idCommand = m_arrCommand.GetAt(iIndex);
			m_toolbar.AddButtons(1, &TBButton);
		}
	}
    
	//Auto Resize Toolbar
	m_toolbar.AutoSize();
}

여기까지 만든 후 실행하면 화면에 툴바가 생성되어 있고, 버튼과, 드롭다운 버튼이 생성 되어 있을 겁니다.

버튼 클릭 메시지 처리

툴바의 버튼을 클릭 했을 때 메시지 처리를 위해 OnBnClickedByIcon 함수를 아래와 같이 정의합니다.

void CMFCApplication1Dlg::OnBnClickedByIcon(UINT uiRes)
{
	CString strMsg = _T("");
	switch (uiRes)
	{
		case WM_COMMAND1:
			strMsg.Format(_T("WM_COMMAND1"));
			break;
		case WM_COMMAND2:
			strMsg.Format(_T("WM_COMMAND2"));
			break;
		case WM_COMMAND3:
			strMsg.Format(_T("WM_COMMAND3"));
			break;
		case WM_COMMAND4:
			strMsg.Format(_T("WM_COMMAND4"));
			break;
		case WM_COMMAND5:
			strMsg.Format(_T("WM_COMMAND5"));
			break;
		case WM_COMMAND6:
			strMsg.Format(_T("WM_COMMAND6"));
			break;
		case WM_COMMAND7:
			strMsg.Format(_T("WM_COMMAND7"));
			break;
		default:
			strMsg = _T("Unknown");
			break;
	}
    
	AfxMessageBox(strMsg);
}

OnBnClickedByIcon 함수는 아래와 같이 메세지 맵에 등록이 되어있어야합니다.

BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
	ON_CONTROL_RANGE(BN_CLICKED, WM_COMMAND1, WM_COMMAND7, CMFCApplication1Dlg::OnBnClickedByIcon)
END_MESSAGE_MAP()
마우스 오버 시 툴팁 표시

툴팁 표시를 위해 OnNotifyToolBar 함수 정의를 해줍니다. 마찬가지로 메시지 맵에 등록되어 있어야합니다.

// 메시지맵
BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
	// ...
	ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &CMFCApplication1Dlg::OnNotifyToolBar)
END_MESSAGE_MAP()

BOOL CMFCApplication1Dlg::OnNotifyToolBar(UINT uiID, NMHDR * pNMHDR, LRESULT * pResult)
{
	if (pNMHDR->code == TTN_NEEDTEXT)
	{
		TOOLTIPTEXT *pTTT = (TOOLTIPTEXT*)pNMHDR;
		CString strToolTip(_T(""));
		switch (pNMHDR->idFrom)
		{
			case WM_COMMAND1:
				strToolTip.Format(_T("WM_COMMAND1"));
				break;
			case WM_COMMAND2:
				strToolTip.Format(_T("WM_COMMAND2"));
				break;
			case WM_COMMAND3:
				strToolTip.Format(_T("WM_COMMAND3"));
				break;
			case WM_COMMAND4:
				strToolTip.Format(_T("WM_COMMAND4"));
				break;
			case WM_COMMAND5:
				strToolTip.Format(_T("WM_COMMAND5"));
				break;
			case WM_COMMAND6:
				strToolTip.Format(_T("WM_COMMAND6"));
				break;
			case WM_COMMAND7:
				strToolTip.Format(_T("WM_COMMAND7"));
				break;
			default:
				strToolTip = _T("");
				break;
		}

		// lpszText
		if (!strToolTip.IsEmpty())
			pTTT->lpszText = (LPTSTR)(LPCTSTR)strToolTip;

		// szText
		//mset(pTTT->szText, 0, sizeof(pTTT->szText));
		// (!strToolTip.IsEmpty())
		//emcpy(pTTT->szText, strToolTip.GetBuffer(), (strToolTip.GetLength() * sizeof(TCHAR)) + 1);
	}
	*pResult = 0;

	return TRUE;
}

상황에 따라 lpszText, szText에 툴팁메시지를 넣어주시면 됩니다. (참고 링크)

드롭다운 클릭시 메뉴 표시

드롭다운 클릭시 메뉴 표시를 위해 아래와 같이 OnToolbarDropDown 함수를 정의해줍니다. 이것 또한 메시지 맵에 등록되어있어야합니다.

// 메시지맵
BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
	// ...
	ON_NOTIFY_EX(TBN_DROPDOWN, IDR_TOOLBAR1, &CMFCApplication1Dlg::OnToolbarDropDown)
END_MESSAGE_MAP()

BOOL CMFCApplication1Dlg::OnToolbarDropDown(UINT uiID, NMHDR * pNMHDR, LRESULT * pResult)
{
	CString strMsg = _T("");
	NMTOOLBAR *pNMTOOLBAR = (NMTOOLBAR *)pNMHDR;
    // 어떤 드롭다운 버튼을 클릭했는지 아이디 체크하려면 
	if (pNMTOOLBAR->iItem != WM_COMMAND6 && pNMTOOLBAR->iItem != WM_COMMAND7)
    	return TRUE;

	// 선택한 드롭박스 아래에 메뉴 생성
	CRect rc = pNMTOOLBAR->rcButton;
	ClientToScreen(rc);
	
	CMenu menu, menu1, menu2;
	menu.CreatePopupMenu();
	menu1.CreatePopupMenu();
	menu2.CreatePopupMenu();

	menu.AppendMenu(MF_POPUP | MF_STRING, (UINT)menu1.GetSafeHmenu(), _T("메뉴1"));
	menu.AppendMenu(MF_POPUP | MF_STRING, (UINT)menu2.GetSafeHmenu(), _T("메뉴2"));
	menu.AppendMenu(MF_STRING, WM_MENU3, _T("메뉴3"));

	// 메뉴1의 하위 메뉴
	menu1.AppendMenu(MF_STRING, WM_MENU1_1, _T("메뉴1_1"));
	menu1.AppendMenu(MF_STRING, WM_MENU1_2, _T("메뉴1_2"));
	menu1.AppendMenu(MF_STRING, WM_MENU1_3, _T("메뉴1_3"));

	// 메뉴2의 하위 메뉴
	menu2.AppendMenu(MF_STRING, WM_MENU2_1, _T("메뉴 2_1"));
	menu2.AppendMenu(MF_STRING, WM_MENU2_2, _T("메뉴 2_2"));
	
	int id = menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD, rc.left, rc.bottom, this, &rc);
	menu1.DestroyMenu();
	menu2.DestroyMenu();
	menu.DestroyMenu();
	
	switch (id)
	{
		case WM_MENU1_1:
			AfxMessageBox(_T("메뉴1_1"));
			break;
		case WM_MENU1_2:
			AfxMessageBox(_T("메뉴1_2"));
			break;
		case WM_MENU1_3:
			AfxMessageBox(_T("메뉴1_3"));
			break;
		case WM_MENU2_1:
			AfxMessageBox(_T("메뉴2_1"));
			break;
		case WM_MENU2_2:
			AfxMessageBox(_T("메뉴2_2"));
			break;
		case WM_MENU3:
			AfxMessageBox(_T("메뉴3"));
			break;
	}

	return TRUE;
}

이상으로 툴바에 대해 알아보았습니다.