#include "stdafx.h" #include "ApredTreeCtrl.h" #define ROFFSET 7 #define WIDE 10 #define WIDE2 5 #define EXPANDED_WIDE 8 #define BADGE_HIDE 0 #define BADGE_DOT 1 #define BADGE_NUMBER 2 #define BADGE_DOT_WIDTH 12 #define BADGE_NUMBER_WIDTH 20 IMPLEMENT_DYNAMIC(CApredTreeCtrl, CTreeCtrl) CApredTreeCtrl::CApredTreeCtrl() { m_hBrushItem[0] = NULL; m_hBrushItem[1] = NULL; m_hBrushItem[2] = NULL; m_hPenItem[0] = nullptr; m_hPenItem[1] = nullptr; m_hPenItem[2] = nullptr; m_hBrushBtn[0] = nullptr; m_hBrushBtn[1] = nullptr; m_hBrushBtn[2] = nullptr; m_crItemBk[0] = RGB(255,255,255); m_crItemBk[1] = RGB(228, 229, 234); m_crItemBk[2] = RGB(236, 237, 241); m_crText[0] = RGB(28, 28, 28); m_crText[1] = RGB(28, 28, 28); m_crText[2] = RGB(28, 28, 28); m_hHoverItem = nullptr; m_bTracking = FALSE; } CApredTreeCtrl::~CApredTreeCtrl() { if (m_hBrushItem[0] != NULL) { ::DeleteObject(m_hBrushItem[0]); } if (m_hBrushItem[1] != NULL) { ::DeleteObject(m_hBrushItem[1]); } if (m_hBrushItem[2] != NULL) { ::DeleteObject(m_hBrushItem[2]); } if (m_hBrushBtn[0] != NULL) { ::DeleteObject(m_hBrushBtn[0]); } if (m_hBrushBtn[1] != NULL) { ::DeleteObject(m_hBrushBtn[1]); } if (m_hBrushBtn[2] != NULL) { ::DeleteObject(m_hBrushBtn[2]); } if (m_hPenItem[0] != nullptr) { ::DeleteObject(m_hPenItem[0]); } if (m_hPenItem[1] != nullptr) { ::DeleteObject(m_hPenItem[1]); } if (m_hPenItem[2] != nullptr) { ::DeleteObject(m_hPenItem[2]); } } BEGIN_MESSAGE_MAP(CApredTreeCtrl, CTreeCtrl) ON_WM_PAINT() ON_WM_MOUSEMOVE() ON_WM_MOUSEHOVER() ON_WM_MOUSELEAVE() ON_WM_TIMER() ON_WM_LBUTTONDOWN() ON_WM_CREATE() END_MESSAGE_MAP() void CApredTreeCtrl::DrawItemButton(HTREEITEM hItem, HDC hDC, CRect* pRect) { // °´Å¥ÒªË¢Ò»Ï POINT pt[3]; if ((GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED) == TVIS_EXPANDED) { int nBottomOffset = (pRect->Height() - EXPANDED_WIDE) / 2; pt[0].x = pRect->right - ROFFSET - EXPANDED_WIDE; pt[0].y = pRect->bottom - nBottomOffset; pt[1].x = pRect->right - ROFFSET; pt[1].y = pRect->bottom - nBottomOffset; pt[2].x = pRect->right - ROFFSET; pt[2].y = pRect->bottom - nBottomOffset - EXPANDED_WIDE; } else { int nBottomOffset = (pRect->Height() - WIDE) / 2; pt[0].x = pRect->right - ROFFSET - WIDE2; pt[0].y = pRect->bottom - nBottomOffset - WIDE; pt[1].x = pRect->right - ROFFSET - WIDE2; pt[1].y = pRect->bottom - nBottomOffset; pt[2].x = pRect->right - ROFFSET; pt[2].y = pRect->bottom - nBottomOffset - WIDE2; } ::Polygon(hDC, pt, 3); } void CApredTreeCtrl::OnPaint() { HDC hDC, hMemDC; HBITMAP hBitmap; RECT rcClient; CString strText; HFONT hFont; HBRUSH hBrushBK; if (m_hBrushItem[0] == nullptr) { m_hBrushItem[0] = CreateSolidBrush(m_crItemBk[0]); } if (m_hBrushItem[1] == nullptr) { m_hBrushItem[1] = CreateSolidBrush(m_crItemBk[1]); } if (m_hBrushItem[2] == nullptr) { m_hBrushItem[2] = CreateSolidBrush(m_crItemBk[2]); } if (m_hBrushBtn[0] == nullptr) { m_hBrushBtn[0] = CreateSolidBrush(m_crText[0]); } if (m_hBrushBtn[1] == nullptr) { m_hBrushBtn[1] = CreateSolidBrush(m_crText[1]); } if (m_hBrushBtn[2] == nullptr) { m_hBrushBtn[2] = CreateSolidBrush(m_crText[2]); } if (m_hPenItem[0] == nullptr) { m_hPenItem[0] = ::CreatePen(PS_SOLID, 1, m_crText[0]); } if (m_hPenItem[1] == nullptr) { m_hPenItem[1] = ::CreatePen(PS_SOLID, 1, m_crText[1]); } if (m_hPenItem[2] == nullptr) { m_hPenItem[2] = ::CreatePen(PS_SOLID, 1, m_crText[2]); } // BeginPaint PAINTSTRUCT ps; hDC = ::BeginPaint(m_hWnd, &ps); GetClientRect(&rcClient); hMemDC = ::CreateCompatibleDC(hDC); hBitmap = ::CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); ::SelectObject(hMemDC, hBitmap); hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); ::SelectObject(hMemDC, hFont); ::SetBkMode(hMemDC, TRANSPARENT); // ±³¾°ÑÕÉ« hBrushBK = CreateSolidBrush(GetBkColor()); ::FillRect(hMemDC, &rcClient, hBrushBK); DeleteObject(hBrushBK); // »æÖÆ×ÓÏî DrawItems(hMemDC); // EndPaint ::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hMemDC, 0, 0, SRCCOPY); ::EndPaint(m_hWnd, &ps); ::DeleteObject(hBitmap); ::DeleteDC(hMemDC); } void CApredTreeCtrl::DrawItems(HDC hDC) { HTREEITEM hCurrentItem; int itemState; CRect rcClient, rcItem, rcText; GetClientRect(&rcClient); Gdiplus::Graphics graphics(hDC); graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); // »æÖƿɼûµÄ×ÓÏî hCurrentItem = GetFirstVisibleItem();//»ñÈ¡µÚÒ»¸ö¿Î¿É¼ûµÄÏî do { if (GetItemRect(hCurrentItem, &rcItem, FALSE) && GetItemRect(hCurrentItem, &rcText, TRUE)) { rcText.right = rcItem.right - 8; rcText.left += 8; CRect fillRect(0, rcItem.top, rcClient.right, rcItem.bottom); if (rcItem.top > rcClient.bottom) { break; } // »æÖÆ×ÓÏî±³¾° itemState = GetItemState(hCurrentItem, TVIF_STATE); COLORREF crText = m_crText[0]; if (itemState & TVIS_SELECTED) { crText = m_crText[1]; HRGN hRgn = CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, 4, 4); ::FillRgn(hDC, hRgn, m_hBrushItem[1]); ::DeleteObject(hRgn); ::SelectObject(hDC, m_hBrushBtn[1]); ::SelectObject(hDC, m_hPenItem[1]); } else if (hCurrentItem == m_hHoverItem) { crText = m_crText[2]; HRGN hRgn = CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, 4, 4); ::FillRgn(hDC, hRgn, m_hBrushItem[2]); ::DeleteObject(hRgn); ::SelectObject(hDC, m_hBrushBtn[2]); ::SelectObject(hDC, m_hPenItem[2]); } else { crText = m_crText[0]; ::SelectObject(hDC, m_hBrushBtn[0]); ::SelectObject(hDC, m_hPenItem[0]); } //»æÖÆÕ¹¿ªÍ¼Æ¬ if (ItemHasChildren(hCurrentItem)) { CRect rcBtn = rcText; rcBtn.right = rcText.left - 2; rcBtn.left -= rcBtn.right - 30; DrawItemButton(hCurrentItem, hDC, &rcBtn); } // ͼ±ê if (m_icons.find(hCurrentItem) != m_icons.end()) { HICON hIcon = m_icons[hCurrentItem]; DrawIconEx(hDC, 8, (rcItem.bottom - rcItem.top - 32) / 2, hIcon, 32, 32, 0, 0, DI_NORMAL); rcText.left += 32; rcText.left += 8; } //»æÖÆÎÄ×Ö HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); ::SelectObject(hDC, hFont); LOGFONT lf; ::GetObject(hFont, sizeof(LOGFONT), &lf); lf.lfWeight = FW_BOLD; HFONT hFontBold = CreateFontIndirect(&lf); ::SetTextColor(hDC, crText); CString strText = GetItemText(hCurrentItem); if (FindBoldItem(hCurrentItem)) { ::SelectObject(hDC, hFontBold); } else { ::SelectObject(hDC, hFont); } DrawText(hDC, strText, strText.GetLength(), &rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE); ::DeleteObject(hFontBold); // ÊÇ·ñÓÐСԲµã if (m_badges.find(hCurrentItem) != m_badges.end()) { BADGE& badge = m_badges[hCurrentItem]; if (badge.type == BADGE_DOT) { Gdiplus::SolidBrush brush(Gdiplus::Color(GetRValue(badge.badgeBackground), GetGValue(badge.badgeBackground), GetBValue(badge.badgeBackground))); int x = rcItem.right - 18 - BADGE_DOT_WIDTH; int y = rcItem.top + (rcItem.Height() - BADGE_DOT_WIDTH) / 2; graphics.FillEllipse(&brush, x, y, BADGE_DOT_WIDTH, BADGE_DOT_WIDTH); } else if (badge.type == BADGE_NUMBER) { Gdiplus::SolidBrush brush(Gdiplus::Color(GetRValue(badge.badgeBackground), GetGValue(badge.badgeBackground), GetBValue(badge.badgeBackground))); int x = rcItem.right - 18 - BADGE_NUMBER_WIDTH; int y = rcItem.top + (rcItem.Height() - BADGE_NUMBER_WIDTH) / 2; graphics.FillEllipse(&brush, x, y, BADGE_NUMBER_WIDTH, BADGE_NUMBER_WIDTH); RECT rcBadge; rcBadge.left = x; rcBadge.right = rcBadge.left + BADGE_NUMBER_WIDTH; rcBadge.top = y; rcBadge.bottom = rcBadge.top + BADGE_NUMBER_WIDTH; ::SetTextColor(hDC, badge.badgeForeground); char szBuffer[32]; sprintf_s(szBuffer, 32, "%d%s", min(badge.number, 9), badge.number > 9 ? "+" : ""); DrawText(hDC, szBuffer, (int)strlen(szBuffer), &rcBadge, DT_CENTER | DT_VCENTER | DT_SINGLELINE); } } } } while ((hCurrentItem = GetNextVisibleItem(hCurrentItem)) != NULL); } void CApredTreeCtrl::OnMouseMove(UINT nFlags, CPoint point) { // ¼ì²â²¢¼Ç¼ÈȵãÏî HTREEITEM hHoverItem = HitTest(point); if (m_hHoverItem != hHoverItem) { m_hHoverItem = hHoverItem; InvalidateRect(NULL); } if (!m_bTracking) { TRACKMOUSEEVENT tme; tme.cbSize = sizeof(tme); tme.dwFlags = TME_HOVER | TME_LEAVE; // ·¢ËÍWM_MOUSEHOVERºÍWM_MOUSELEAVE tme.hwndTrack = m_hWnd; // Ö¸¶¨Òª×·×ٵĴ°¿Ú tme.dwHoverTime = 10; // Êó±êÔÚ°´Å¥ÉÏÍ£Áô³¬¹ý10ms£¬²ÅÈÏΪ״̬ΪHOVER m_bTracking = _TrackMouseEvent(&tme); // ¿ªÆôWindowsµÄWM_MOUSELEAVEWM_MOUSEHOVERʼþÖ§³Ö } CTreeCtrl::OnMouseMove(nFlags, point); } void CApredTreeCtrl::OnTimer(UINT_PTR nIDEvent) { if (1 == nIDEvent) { BOOL bNeedInvalidate = FALSE; for (auto& item : m_badges) { if (item.second.showTime > 0) { item.second.showTime--; if (item.second.showTime == 0) { item.second.type = BADGE_HIDE; bNeedInvalidate = TRUE; } } } if(bNeedInvalidate) InvalidateRect(NULL, TRUE); } CTreeCtrl::OnTimer(nIDEvent); } void CApredTreeCtrl::OnMouseHover(UINT nFlags, CPoint point) { } void CApredTreeCtrl::OnMouseLeave() { m_hHoverItem = nullptr; m_bTracking = FALSE; // ÈôÒѾ­À뿪£¬ÔòÍ£Ö¹×·×Ù InvalidateRect(NULL); CTreeCtrl::OnMouseLeave(); } void CApredTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point) { HTREEITEM hItem = HitTest(point); if (hItem != nullptr) { SelectItem(hItem); GetParent()->SendMessage(ID_MSG_TREE_CLICK_ITEM, (WPARAM)hItem, 0); } CTreeCtrl::OnLButtonDown(nFlags, point); } void CApredTreeCtrl::SetItemBadge(HTREEITEM hItem, COLORREF badgeBackground, COLORREF badgeForeground) { if (m_badges.find(hItem) == m_badges.end()) { BADGE badge; badge.badgeBackground = badgeBackground; badge.badgeForeground = badgeForeground; badge.type = BADGE_HIDE; badge.showTime = 0; m_badges[hItem] = badge; } else { BADGE& badge = m_badges[hItem]; badge.badgeBackground = badgeBackground; badge.badgeForeground = badgeForeground; badge.type = BADGE_HIDE; } } void CApredTreeCtrl::ShowItemBadgeNumber(HTREEITEM hItem, int number) { if (m_badges.find(hItem) == m_badges.end()) { return; } BADGE& badge = m_badges[hItem]; badge.type = BADGE_NUMBER; badge.number = number; InvalidateRect(NULL, TRUE); } void CApredTreeCtrl::ShowItemBadgeDotMode(HTREEITEM hItem, int nSecond/* = 0*/) { if (m_badges.find(hItem) == m_badges.end()) { return; } BADGE& badge = m_badges[hItem]; if (badge.type != BADGE_DOT) { badge.type = BADGE_DOT; badge.showTime = nSecond; InvalidateRect(NULL, TRUE); } } void CApredTreeCtrl::HideItemBadge(HTREEITEM hItem) { BADGE& badge = m_badges[hItem]; if (badge.type != BADGE_HIDE) { badge.type = BADGE_HIDE; badge.showTime = 0; InvalidateRect(NULL, TRUE); } } void CApredTreeCtrl::SetItemBold(HTREEITEM item) { if (!FindBoldItem(item)) { m_itemBolds.push_back(item); } } void CApredTreeCtrl::CancelItemBold(HTREEITEM item) { for (auto iter = m_itemBolds.begin(); iter != m_itemBolds.end(); iter++) { if (*iter == item) { m_itemBolds.erase(iter); break; } } InvalidateRect(NULL, TRUE); } BOOL CApredTreeCtrl::FindBoldItem(HTREEITEM item) { BOOL bFound = FALSE; for (auto i : m_itemBolds) { if (i == item) return TRUE; } return FALSE; } int CApredTreeCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CTreeCtrl::OnCreate(lpCreateStruct) == -1) return -1; SetTimer(1, 1000, nullptr); return 0; } void CApredTreeCtrl::PreSubclassWindow() { SetTimer(1, 1000, nullptr); CTreeCtrl::PreSubclassWindow(); } void CApredTreeCtrl::SetItemIcon(HTREEITEM hItem, HICON hIcon) { m_icons[hItem] = hIcon; }